Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add StaticStates wrapper #377

Merged
merged 25 commits into from Dec 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cb165ca
chore: Refactor canvasProvider and extract util function
mannycarrera4 Dec 16, 2019
acad35d
chore: Rename function
mannycarrera4 Dec 16, 2019
47e16e0
fix: Rename prop name
mannycarrera4 Dec 16, 2019
0ae64b3
fix: Remove unused import
mannycarrera4 Dec 16, 2019
e2eb14d
fix: Remove space added
mannycarrera4 Dec 16, 2019
76bac48
chore: Rebase and rename prop
mannycarrera4 Dec 16, 2019
f48e7ce
fix: Fix merging conflicts
mannycarrera4 Dec 16, 2019
d49dc80
fix: Rename prop for decorator
mannycarrera4 Dec 16, 2019
39a06f1
fix: Rename prop and add readme
mannycarrera4 Dec 17, 2019
b57196e
feat: Add wrapper for staticState utility function
NicholasBoll Dec 17, 2019
cf4e352
fix: Add tests for changeToStaticStates and refactor decorator
mannycarrera4 Dec 18, 2019
45b512b
fix: Remove storybook parameter for testing
mannycarrera4 Dec 18, 2019
7e311d8
fix: Remove test code for buttons
mannycarrera4 Dec 18, 2019
c845fde
fix: Sort state stories last
mannycarrera4 Dec 18, 2019
5c8e2db
fix: Rename function for converting pseudo to class
mannycarrera4 Dec 19, 2019
cd0d94e
fix: Remove console.log and add back in stories
mannycarrera4 Dec 19, 2019
7d5c375
fix: Change styles to interpolation
mannycarrera4 Dec 19, 2019
10285a0
test: Add test for &:state
mannycarrera4 Dec 19, 2019
63821d7
fix: Change name of interface
mannycarrera4 Dec 19, 2019
d904496
fix: Nicholas is dope
mannycarrera4 Dec 19, 2019
4e74004
Merge branch 'master' into mc-update-canvas-provider
mannycarrera4 Dec 20, 2019
4b2952b
chore: Removed unneeded type from styled.ts
NicholasBoll Dec 20, 2019
d71362a
feat: Move static state conversion into our theme
NicholasBoll Dec 20, 2019
413e5b6
fix: Remove disable input provider prop
mannycarrera4 Dec 21, 2019
eb73ce4
Merge branch 'master' into mc-update-canvas-provider
NicholasBoll Dec 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions .storybook/config.js
Expand Up @@ -35,19 +35,26 @@ addDecorator(FontsDecorator);
addDecorator(CanvasProviderDecorator);

/** If the string contains a phrase, prefix it. This is useful for making ordering sections */
const prefix = (phrase, prefix) => value => (value.indexOf(phrase) > -1 ? prefix + value : value);
const prefix = (phrase, prefix) => (/** @type {string} */ value) => {
const index = value.indexOf(phrase);
return index > -1 ? value.substr(0, index) + prefix + value.substr(index) : value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change allows for multi-tier sorting instead of a global sorting. For example, "Welcome" is the category and "getting-started" is the item. The below will produce a string like:

"0welcome--agetting-started"

Since the "a" is now added before "getting-started" instead of before "welcome", it allows sorting to be local to other items. Lowercase letters should be used in multi-tier sorting so it flows naturally with other items without disrupting them. For example, numbers are always before lowercase letters in ASCII. So even if you add a '9', it will sort first rather than the desired last which is why z is used below

};
const pipe = (...fns) => value => fns.reduce((result, fn) => fn(result), value);

function storySort(a, b) {
const prefixFn = pipe(
prefix('welcome-', '0'),
prefix('getting-started', '0'),
prefix('getting-started', 'a'),
prefix('tokens-', '1'),
prefix('components-', '2'),
prefix('labs-', '3')
prefix('labs-', '3'),
prefix('default', 'aa'),
prefix('states', 'zz')
);

const left = prefixFn(a[0]);
const right = prefixFn(b[0]);

return left === right ? 0 : left.localeCompare(right);
}

Expand Down
11 changes: 11 additions & 0 deletions modules/_labs/core/react/lib/StaticStates.tsx
@@ -0,0 +1,11 @@
import * as React from 'react';
import {useTheme} from './theming/useTheme';
import CanvasProvider from './CanvasProvider';
import {CanvasTheme} from './theming';

export const StaticStates: React.FC = ({children}) => {
const theme: CanvasTheme & {_staticStates?: boolean} = useTheme();
theme._staticStates = true;

return <CanvasProvider theme={theme}>{children}</CanvasProvider>;
};
27 changes: 23 additions & 4 deletions modules/_labs/core/react/lib/theming/styled.ts
Expand Up @@ -7,21 +7,40 @@ const noop = (styles: any) => styles;
// Pulled from https://github.com/emotion-js/emotion/blob/master/packages/styled-base/src/utils.js#L6 (not exported)
type Interpolations = Array<any>;

export const convertToStaticStates = (obj?: CSSObject): CSSObject | undefined => {
if (!obj) {
return obj;
}

return Object.keys(obj).reduce((result, key) => {
const newKey = key
.replace(/^:/, '&:') // handle shorthand like ":focus"
.replace(/,(\s+):/g, ',$1&:') // handle selectors like ":focus, :hover"
.replace(/:(focus|hover|active)/g, '.$1')
.replace(/\[data\-whatinput="?(mouse|touch|keyboard|pointer)"?]/g, '[data-whatinput="noop"]');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disables the InputProvider style overrides

const value =
typeof obj[key] === 'object' ? convertToStaticStates(obj[key] as CSSObject) : obj[key];
const newObj = {...result, [newKey]: value};
return newObj;
}, {});
};

function styled<Props>(node: any) {
return (...args: Interpolation<Props>[]) => {
const newArgs: Interpolations = args.map(
interpolation => (props: Props & {theme: CanvasTheme; direction: ContentDirection}) => {
interpolation => (props: Props & {theme: CanvasTheme & {_staticStates?: boolean}}) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not adding _staticStates to CanvasTheme keeps it private

props.theme = useTheme(props.theme);
const direction = props.theme.direction;
const maybeFlip = direction === ContentDirection.RTL ? rtlCSSJS : noop;
const maybeConvert = props.theme._staticStates ? convertToStaticStates : noop;

try {
if (typeof interpolation === 'function') {
return maybeFlip(interpolation(props) as CSSObject);
return maybeFlip(maybeConvert(interpolation(props)) as CSSObject);
}
return maybeFlip(interpolation as CSSObject);
return maybeFlip(maybeConvert(interpolation) as CSSObject);
} catch (e) {
return interpolation;
return maybeConvert(interpolation);
}
}
);
Expand Down
134 changes: 134 additions & 0 deletions modules/_labs/core/react/spec/styled.spec.ts
@@ -0,0 +1,134 @@
import {convertToStaticStates} from '../lib/theming/styled';

describe('changeToStaticStates', () => {
it('should convert ":hover" to "&.hover"', () => {
const input = {':hover': {display: 'none'}};
const expected = {
'&.hover': {
display: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert ":active" to "&.active"', () => {
const input = {':active': {display: 'none'}};
const expected = {
'&.active': {
display: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert ":focus" to "&.focus"', () => {
const input = {':focus': {display: 'none'}};
const expected = {
'&.focus': {
display: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert "&:hover" to "&.hover"', () => {
const input = {'&:hover': {display: 'none'}};
const expected = {
'&.hover': {
display: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert "&:focus" to "&.focus"', () => {
const input = {'&:focus': {display: 'none'}};
const expected = {
'&.focus': {
display: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert "div:focus" to "div.focus"', () => {
const input = {'div:focus': {display: 'none'}};
const expected = {
'div.focus': {
display: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert nested object', () => {
const input = {
div: {
span: {
':hover': {
display: 'none',
},
},
},
};
const expected = {
div: {
span: {
'&.hover': {
display: 'none',
},
},
},
};

expect(convertToStaticStates(input)).toEqual(expected);
});

it('should convert multi-selectors', () => {
const input = {
[`:hover, :focus`]: {
display: 'none',
},
};
const expected = {
[`&.hover, &.focus`]: {
display: 'none',
},
};

expect(convertToStaticStates(input)).toEqual(expected);
});

it('should not convert :not or :disabled', () => {
const input = {
'&:not(:disabled)': {
display: 'none',
},
};
const expected = {
'&:not(:disabled)': {
display: 'none',
},
};

expect(convertToStaticStates(input)).toEqual(expected);
});

it('should handle falsy objects', () => {
expect(convertToStaticStates(undefined)).toEqual(undefined);
});

it('should remove data-whatinput modifiers', () => {
const input = {
'[data-whatinput="mouse"], [data-whatinput=keyboard], [data-whatinput="pointer"], [data-whatinput="touch"]': {
outline: 'none',
},
};
const expected = {
'[data-whatinput="noop"], [data-whatinput="noop"], [data-whatinput="noop"], [data-whatinput="noop"]': {
outline: 'none',
},
};
expect(convertToStaticStates(input)).toEqual(expected);
});
});