From 3f5056d8fe5e417c121d8ee0d92daeeaaca40766 Mon Sep 17 00:00:00 2001 From: devDobby Date: Tue, 11 Jun 2024 11:07:39 +0200 Subject: [PATCH] Show style applied on tag (p, span, div) and private selectors as parent rules instead of hiding them (#5890) * GrapesJS can now returns multiple CSS rules class as parents and CSS rule applied to tag name * Take last version of grape. Fix bug with private selectors that stay invisible in style manager when another class was applied. * Fix reviews; * Simplify code to work only with single tag name; --------- Co-authored-by: Artur Arseniev --- src/style_manager/index.ts | 32 +++++++++++++-- test/specs/style_manager/index.ts | 66 +++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/style_manager/index.ts b/src/style_manager/index.ts index 196c20944..38044baca 100644 --- a/src/style_manager/index.ts +++ b/src/style_manager/index.ts @@ -597,6 +597,8 @@ export default class StyleManager extends ItemManagerModule< const cmp = target.toHTML ? target : target.getComponent(); const optsSel = { array: true } as const; let cmpRules: CssRule[] = []; + let tagNameRules: CssRule[] = []; + let invisibleAndOtherRules: CssRule[] = []; let otherRules: CssRule[] = []; let rules: CssRule[] = []; @@ -605,19 +607,41 @@ export default class StyleManager extends ItemManagerModule< ? [] : cssC.getRules().filter(rule => { const rSels = rule.getSelectors().map(s => s.getFullName()); - return !!rSels.length && rSels.every(rSel => values.indexOf(rSel) >= 0); + + // rSels is equal to 0 when rule selectors contain tagName like : p {}, .logo path {}, ul li {} + if (rSels.length === 0) { + return false; + } + + return rSels.every(rSel => values.indexOf(rSel) >= 0); }); }; + const rulesByTagName = (tagName: string) => { + return !tagName ? [] : cssC.getRules().filter(rule => rule.selectorsToString() === tagName); + }; + // Componente related rule if (cmp) { cmpRules = cssC.getRules(`#${cmp.getId()}`); + tagNameRules = rulesByTagName(cmp.get('tagName')); otherRules = sel ? rulesBySelectors(sel.getSelectors().getFullName(optsSel)) : []; - rules = otherRules.concat(cmpRules); + rules = otherRules.concat(tagNameRules).concat(cmpRules); } else { cmpRules = sel ? cssC.getRules(`#${sel.getId()}`) : []; - otherRules = rulesBySelectors(target.getSelectors().getFullName(optsSel)); - rules = cmpRules.concat(otherRules); + tagNameRules = rulesByTagName(sel?.get('tagName') || ''); + // Get rules set on invisible selectors like private one + const allCmpClasses = sel?.getSelectors().getFullName(optsSel) || []; + const invisibleSel = allCmpClasses.filter( + (item: string) => + target + .getSelectors() + .getFullName(optsSel) + .findIndex(sel => sel === item) === -1 + ); + // Get rules set on active and visible selectors + invisibleAndOtherRules = rulesBySelectors(invisibleSel.concat(target.getSelectors().getFullName(optsSel))); + rules = tagNameRules.concat(cmpRules).concat(invisibleAndOtherRules); } const all = rules diff --git a/test/specs/style_manager/index.ts b/test/specs/style_manager/index.ts index d3d8c9c08..9eb0bf937 100644 --- a/test/specs/style_manager/index.ts +++ b/test/specs/style_manager/index.ts @@ -195,6 +195,72 @@ describe('StyleManager', () => { expect(obj.getSelectedParents()).toEqual([rule1]); }); + test('With multiple classes, should both be in parents list', () => { + const cmp = domc.addComponent('
'); + const [rule1, rule2] = cssc.addRules(` + .cls { color: red; } + .cls2 { color: blue; } + `); + em.setSelected(cmp); + obj.__upSel(); + expect(obj.getSelected()).not.toBe(rule1); + expect(obj.getSelected()).not.toBe(rule2); + expect(obj.getSelectedParents()).toEqual([rule2, rule1]); + }); + + test('With tagName + class, class first', () => { + const cmp = domc.addComponent('
'); + const [rule1, rule2] = cssc.addRules(` + .cls { color: red; } + div { color: yellow; } + `); + em.setSelected(cmp); + obj.__upSel(); + expect(obj.getSelected()).toBe(rule1); + expect(obj.getSelectedParents()).toEqual([rule2]); + }); + + test('Should ignore rules with tagName in the selector path but the rule is not apply on the tagName', () => { + const cmp = domc.addComponent('
'); + const [rule1, rule2] = cssc.addRules(` + .cls { color: red; } + div { color: yellow; } + div .child { padding: 10px; } + `); + em.setSelected(cmp); + obj.__upSel(); + // getSelectedParents should only have 1 rule as the third one is not applied on the div + expect(obj.getSelected()).toBe(rule1); + expect(obj.getSelectedParents()).toEqual([rule2]); + }); + + test('Should tagName rules if the selectors does not contain only the tagNale', () => { + const cmp = domc.addComponent('
'); + const [rule1, rule2] = cssc.addRules(` + .cls { color: red; } + div { color: yellow; } + .child div { padding: 10px; } + `); + em.setSelected(cmp); + obj.__upSel(); + // getSelectedParents should only have 1 rule as the third one is not applied on the div + expect(obj.getSelected()).toBe(rule1); + expect(obj.getSelectedParents()).toEqual([rule2]); + }); + + test('With tagName + ID + class, class first, ID second', () => { + const cmp = domc.addComponent('
'); + const [rule1, rule2, rule3] = cssc.addRules(` + .cls { color: red; } + div { color: yellow; } + #id-test { color: blue; } + `); + em.setSelected(cmp); + obj.__upSel(); + expect(obj.getSelected()).toBe(rule1); + expect(obj.getSelectedParents()).toEqual([rule3, rule2]); + }); + test('With ID + class, multiple devices', () => { sm.setComponentFirst(true); const cmp = domc.addComponent('
');