Skip to content

Commit

Permalink
feat(react): grid layout working, permutations correctly showing up (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
ES-tarweiler committed Nov 3, 2020
1 parent c1ef260 commit 9bc4b13
Showing 1 changed file with 235 additions and 83 deletions.
318 changes: 235 additions & 83 deletions src/react/react.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import {
} from '../share/constants';
import {
AttributesStatesDefault,
Orientation,
PseudoState,
PseudoStates,
PseudoStatesDefault,
PseudoStatesDefaultPrefix,
PseudoStatesParameters,
Expand All @@ -25,6 +27,14 @@ import { SAPS_BUTTON_CLICK, SAPS_INIT_PSEUDO_STATES } from '../share/events';
import { getMixedPseudoStates, sanitizePseudoName } from '../share/utils';
import { AttributeStatesObj } from '../share/AttributeStatesObj';
import { PermutationStatsObj } from '../share/PermutationsStatesObj';
import { styles } from '../share/styles';

interface IState {
name: string;
// eslint-disable-next-line no-undef
states: Array<JSX.Element>;
permutation?: PermutationStatsObj;
}

function pseudoStateFn(
getStory: StoryGetter,
Expand Down Expand Up @@ -81,96 +91,88 @@ function pseudoStateFn(
// Maybe not editable by user in angular context?
parameters.prefix =
parameters?.prefix || options?.prefix || PseudoStatesDefaultPrefix;
// eslint-disable-next-line no-undef
const states: Array<JSX.Element> = [];

// create story's new template
if (parameters.pseudos) {
for (const state of parameters.pseudos) {
const pstate = sanitizePseudoName(state);
// const storyState = {...story, props: {...story.props, [state]: true}};
const states: IState[] = [];

const pseudoStoryPart = (
<div
className={`pseudo-states-addon__story pseudo-states-addon__story--${pstate}`}
key={`pseudo-${pstate}`}
>
<div className="pseudo-states-addon__story__header">{state}:</div>
<div className="pseudo-states-addon__story__container">{story}</div>
</div>
);

states.push(pseudoStoryPart);
}
}
if (attributesAsObject) {
for (const attr of attributesAsObject) {
const storyState = {
...story,
props: { ...story.props, [attr.attr]: attr.value },
};

states.push(
<div
className={`pseudo-states-addon__story pseudo-states-addon__story--attr-${attr.attr}`}
key={`attr-${attr.attr}`}
>
<div className="pseudo-states-addon__story__header">{attr.attr}:</div>
<div className="pseudo-states-addon__story__container">
{storyState}
</div>
</div>
);
}
// Create Normal states
states.push({
name: 'Normal',
states: createPseudos(story, parameters.pseudos, attributesAsObject),
});
// create story's new template
for (const permutation of permutationsAsObject) {
states.push({
name: permutation.attr,
permutation,
states: createPseudos(
story,
parameters.pseudos,
attributesAsObject,
permutation
),
});
}

// update pseudo states after story is rendered
const updatePseudoStates = () => {
if (parameters.pseudos) {
for (const pstateRaw of parameters.pseudos) {
const pstate = sanitizePseudoName(pstateRaw);
for (const permutation of permutationsAsObject) {
for (const pstateRaw of parameters.pseudos) {
const pstate = sanitizePseudoName(pstateRaw);
const applyPseudoStateToHost = (
containerTmp: Element,
selectorTmp: Selector | null
) => {
let host;
if (!selectorTmp) {
host = containerTmp.children[0];
} else if (typeof selectorTmp === 'string') {
host = containerTmp.querySelector(selectorTmp);
} else if (Array.isArray(selectorTmp)) {
for (const s of selectorTmp as Array<PseudoState>) {
applyPseudoStateToHost(containerTmp, s);
}
}
// get css module [path][name]__[local] and remove [local]
// TODO test if first class represents always css module
let moduleClass = null;
// try to find css module prefix
if (host?.classList[0]) {
moduleClass = host?.classList[0].match(/(.+?)?__/) as Array<
string
>;
}

const container = document.querySelector(
`.pseudo-states-addon__story--${pstate} .pseudo-states-addon__story__container`
);
let cssModulePrefix = '';
if (moduleClass && moduleClass?.length >= 1) {
cssModulePrefix = `${moduleClass[1]}__`;
}

const applyPseudoStateToHost = (
containerTmp: Element,
selectorTmp: Selector | null
) => {
let host;
if (!selectorTmp) {
host = containerTmp.children[0];
} else if (typeof selectorTmp === 'string') {
host = containerTmp.querySelector(selectorTmp);
} else if (Array.isArray(selectorTmp)) {
for (const s of selectorTmp as Array<PseudoState>) {
applyPseudoStateToHost(containerTmp, s);
const subPseudoStates = getMixedPseudoStates(pstateRaw).concat([
permutation.attr,
]);
for (const s of subPseudoStates) {
host?.classList.add(
`${cssModulePrefix}${parameters.prefix}${s.trim()}`
);
}
}
// get css module [path][name]__[local] and remove [local]
// TODO test if first class represents always css module
let moduleClass = null;
// try to find css module prefix
if (host?.classList[0]) {
moduleClass = host?.classList[0].match(/(.+?)?__/) as Array<string>;
}
};

let cssModulePrefix = '';
if (moduleClass && moduleClass?.length >= 1) {
cssModulePrefix = `${moduleClass[1]}__`;
}
const containers = document.querySelectorAll(
`.pseudo-states-addon__story--${pstate} .pseudo-states-addon__story__container`
);

const subPseudoStates = getMixedPseudoStates(pstateRaw);
for (const s of subPseudoStates) {
host?.classList.add(
`${cssModulePrefix}${parameters.prefix}${s.trim()}`
);
/**
* For each story container found (one for every permutation present)
* cycle through the peudostate elements and add the pseudo state to them
*/
if (containers && containers.length > 0) {
containers.forEach((container) => {
if (container) {
applyPseudoStateToHost(container, selector);
}
});
}
};

if (container) {
applyPseudoStateToHost(container, selector);
}
}
}
Expand All @@ -194,19 +196,169 @@ function pseudoStateFn(
channel.removeAllListeners(SAPS_BUTTON_CLICK);
});

const numberOfPseudos = parameters.pseudos ? parameters.pseudos.length : 0;
const numberOfAttributes = parameters.attributes
? parameters.attributes.length
: 0;

/**
* Grid can either be row or column oriented, depening on the
* parameters.styles variable (ROW or COLUMN). We flip the
* row / column count when ROW was given.
*/
const getGridStyles = () => {
switch (parameters.styles) {
case Orientation.ROW:
return {
gridTemplateRows: `repeat(${
1 + permutationsAsObject.length
}, min-content)`,
gridTemplateColumns: `repeat(${
1 + numberOfPseudos + numberOfAttributes
}, max-content)`,
gridAutoFlow: 'row',
};

case Orientation.COLUMN:
default:
return {
gridTemplateRows: `repeat(${
1 + numberOfPseudos + numberOfAttributes
}, min-content)`,
gridTemplateColumns: `repeat(${
1 + permutationsAsObject.length
}, min-content)`,
gridAutoFlow: 'column',
};
}
};

return isStoryDisabled ? (
story
) : (
<div className="pseudo-states-addon__container">
<div className="pseudo-states-addon__story pseudo-states-addon__story--Normal">
<div className="pseudo-states-addon__story__header">Normal:</div>
<div className="pseudo-states-addon__story__container">{story}</div>
</div>
{states}
<div
className="pseudo-states-addon__container"
style={{ ...styles.gridContainer, ...getGridStyles() }}
>
{/**
* Map over all states and return a Fragment (will vanish in DOM) with the
* full story including the normal state which is defined here
*/}
{states.map((state: IState) => (
<>
<div
className={`pseudo-states-addon__story pseudo-states-addon__story--${state.name}`}
>
<div className="pseudo-states-addon__story__header">
{state.name}:
</div>
<div className="pseudo-states-addon__story__container">
{/**
* Need to add permutation attributes to the normal state,
* therefore we clone the element and add the necessary props
*/}
{React.cloneElement(
story,
state.permutation
? {
[state.permutation.attr]: state.permutation.value,
}
: {}
)}
</div>
</div>
{state.states}
</>
))}
</div>
);
}

/**
* Given Pseudo States and Attributes, create all necessary
* react element children and push them to a JSX Array to
* return them to be rendered.
*/
const createPseudos = (
story: any,
pseudos: PseudoStates | undefined,
attributesAsObject: Array<AttributeStatesObj>,
permutation?: PermutationStatsObj
) => {
// eslint-disable-next-line no-undef
const states: Array<JSX.Element> = [];

if (pseudos) {
// Run through pseudo states and render a story element for each
for (const state of pseudos) {
const pstate = sanitizePseudoName(state);

// When a permutation was given, add it to the story props
const storyState = {
...story,
props: permutation
? {
...story.props,
[state]: true,
[permutation.attr]: permutation.value,
}
: {
...story.props,
[state]: true,
},
};

const pseudoStoryPart = (
<div
className={`pseudo-states-addon__story pseudo-states-addon__story--${pstate}`}
key={`pseudo-${pstate}`}
>
<div className="pseudo-states-addon__story__header">{state}:</div>
<div className="pseudo-states-addon__story__container">
{storyState}
</div>
</div>
);

states.push(pseudoStoryPart);
}
if (attributesAsObject) {
// Run through the given attributes and add a story element for each
for (const attr of attributesAsObject) {
// When a permutation was given, add it to the story props
const storyState = {
...story,
props: permutation
? {
...story.props,
[attr.attr]: attr.value,
[permutation.attr]: permutation.value,
}
: {
...story.props,
[attr.attr]: attr.value,
},
};

states.push(
<div
className={`pseudo-states-addon__story pseudo-states-addon__story--attr-${attr.attr}`}
key={`attr-${attr.attr}`}
>
<div className="pseudo-states-addon__story__header">
{attr.attr}:
</div>
<div className="pseudo-states-addon__story__container">
{storyState}
</div>
</div>
);
}
}
}
return states;
};

export const withPseudo = makeDecorator({
...addonParameters,
wrapper: (
Expand Down

0 comments on commit 9bc4b13

Please sign in to comment.