Web related enhancements to merge and simplified tokens (#50)

* Make token types easier to use

* fix bug with null token arrays after typing change

* ensure styles merge selectors and merge handles className

* Change files

* switch flatten to use array mapping
This commit is contained in:
Jason Morse 2019-10-30 13:13:55 -07:00 коммит произвёл GitHub
Родитель b7349c6471
Коммит 6a408c2ade
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 94 добавлений и 17 удалений

Просмотреть файл

@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "ensure styles merge selectors and merge handles className",
"packageName": "@uifabricshared/foundation-settings",
"email": "jasonmo360@gmail.com",
"commit": "30d9a31ad57f098ab1b6074fd975dc157e64fdc8",
"date": "2019-10-30T07:05:21.836Z"
}

Просмотреть файл

@ -0,0 +1,8 @@
{
"type": "patch",
"comment": "Make token types easier to use",
"packageName": "@uifabricshared/foundation-tokens",
"email": "jasonmo360@gmail.com",
"commit": "30d9a31ad57f098ab1b6074fd975dc157e64fdc8",
"date": "2019-10-30T07:05:33.247Z"
}

Просмотреть файл

@ -5,6 +5,7 @@ import { IStyleProp } from './Styles.types';
interface IProps {
root: {
prop1: string;
className?: string;
style: IStyleProp<{
fontFamily?: string;
fontWeight?: 'light' | 'normal' | 'bold';
@ -19,6 +20,7 @@ interface IProps {
const settingsDefault: IComponentSettings<IProps> = {
root: {
className: 'foo bar',
style: {
fontFamily: 'Calibri',
fontWeight: 'normal',
@ -30,6 +32,7 @@ const settingsDefault: IComponentSettings<IProps> = {
const settingsBase: IComponentSettings<IProps> = {
root: {
className: 'baz',
style: {
fontWeight: 'bold',
fontSize: 16,
@ -105,6 +108,7 @@ describe('Merge settings tests', () => {
const merged = mergeSettings(settingsDefault, settingsBase);
expect(merged).toEqual({
root: {
className: 'foo bar baz',
style: {
fontFamily: 'Calibri',
fontWeight: 'bold',
@ -137,6 +141,7 @@ describe('Merge settings tests', () => {
const merged = mergeSettings(settingsBase, settingsNormal);
expect(merged).toEqual({
root: {
className: 'baz',
style: {
fontFamily: 'Calibri Body',
fontWeight: 'bold',
@ -161,6 +166,7 @@ describe('Merge settings tests', () => {
const merged = mergeSettings(settingsDefault, settingsBase, settingsNormal);
expect(merged).toEqual({
root: {
className: 'foo bar baz',
style: {
fontFamily: 'Calibri Body',
fontWeight: 'bold',

Просмотреть файл

@ -15,6 +15,10 @@ function _mergeStyles(_options: IMergeOptions, ...objs: (IStyleProp<object>)[]):
return mergeAndFlattenStyles(undefined, undefined, ...objs);
}
function _mergeClassName(_options: IMergeOptions, ...names: any[]): string | undefined {
return names.filter(v => v && typeof v === 'string').join(' ');
}
/**
* styles should be flattened and merged
* tokens should be merged one level
@ -23,6 +27,7 @@ function _mergeStyles(_options: IMergeOptions, ...objs: (IStyleProp<object>)[]):
const _recurseOptions = {
style: _mergeStyles,
tokens: true,
className: _mergeClassName,
_overrides: _mergeCollection
};
@ -43,7 +48,11 @@ const _mergeCollectionOptions: IMergeOptions = {
};
const _mergePropsOptions: IMergeOptions = {
recurse: { style: _mergeStyles, tokens: true }
recurse: {
style: _mergeStyles,
tokens: true,
className: _mergeClassName
}
};
/**

Просмотреть файл

@ -24,6 +24,7 @@ interface IFakeStyle {
color?: string;
fontFamily?: string;
borderWidth?: number;
':hover'?: IFakeStyle;
}
const styleFinalizer: IFinalizeStyle = (target: IFakeStyle) => {
@ -96,6 +97,36 @@ const sMergedFinal: IFakeStyleProp = {
...s2Final
};
const sSelector: IFakeStyleProp = {
borderWidth: 1,
':hover': {
borderWidth: 2,
fontFamily: 'primary'
}
};
const sSelector2: IFakeStyleProp = {
backgroundColor: 'white',
':hover': {
backgroundColor: 'black',
borderWidth: 3
}
};
const sArraySelector: IFakeStyleProp = [[sSelector]];
const sArraySelector2: IFakeStyleProp = [sSelector2];
const sMergedSelectors: IFakeStyleProp = {
borderWidth: 1,
backgroundColor: 'white',
':hover': {
borderWidth: 3,
fontFamily: 'primary',
backgroundColor: 'black'
}
};
describe('Style flatten and merge tests', () => {
test('flatten recursive arrays', () => {
const flattened = flattenStyle(s1);
@ -113,6 +144,16 @@ describe('Style flatten and merge tests', () => {
expect(merged).toEqual(sMerged);
});
test('merge with sub objects', () => {
const merged = mergeAndFlattenStyles(undefined, undefined, sSelector, sSelector2);
expect(merged).toEqual(sMergedSelectors);
});
test('merge sub objects in arrays', () => {
const merged = mergeAndFlattenStyles(undefined, undefined, sArraySelector, sArraySelector2);
expect(merged).toEqual(sMergedSelectors);
});
test('finalize single style', () => {
const final = mergeAndFlattenStyles(styleFinalizer, s1);
expect(final).toEqual(s1flattenFinal);

Просмотреть файл

@ -15,12 +15,7 @@ export function flattenStyle(style: IStyleProp<object>): object {
if (!Array.isArray(style)) {
return style;
}
const result = {};
for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
Object.assign(result, flattenStyle(style[i]));
}
return result;
return immutableMerge(...style.map(v => flattenStyle(v)));
}
/**

Просмотреть файл

@ -141,16 +141,23 @@ export function buildComponentTokens<TProps extends object, TTheme>(
// iterate through each factory and generate a handler for it. Note that even if no styleFactories
// are provided within it will still generate the handler to do style caching and finalization
Object.getOwnPropertyNames(factories).forEach(slot => {
const factorySet = factories[slot].styleFactories;
const factoriesBase = factories[slot].styleFactories;
const mappings: ITokensForSlot<TProps, TTheme> = { toStyle: [], toTokens: [], functions: [] };
const { toStyle, toTokens, functions } = mappings;
const slotKeys = {};
// if there are style factories provided split them into ones that target tokens and ones that target styles
if (factorySet) {
if (factoriesBase) {
const factorySet = Array.isArray(factoriesBase) ? factoriesBase : [factoriesBase];
for (const set of factorySet) {
if (Array.isArray(set)) {
for (const operation of set) {
if (typeof set === 'function') {
functions.push(set);
set._keys.forEach(key => {
slotKeys[key as string] = undefined;
});
} else {
const setArray = Array.isArray(set) ? set : [set as IStyleFactoryOperation<TProps, TTheme>];
for (const operation of setArray) {
slotKeys[operation.source as string] = undefined;
const target = operation.target || operation.source;
if (hasToken && hasToken(slot, target as string)) {
@ -159,11 +166,6 @@ export function buildComponentTokens<TProps extends object, TTheme>(
toStyle.push(operation);
}
}
} else {
functions.push(set);
set._keys.forEach(key => {
slotKeys[key as string] = undefined;
});
}
}
}

Просмотреть файл

@ -50,11 +50,19 @@ export type IOperationSet<TProps, TTheme> = IStyleFactoryOperation<TProps, TThem
export type IStyleFactoryFunctionRaw<TProps, TTheme> = (tokenProps: TProps, theme: TTheme) => TProps;
export type IStyleFactoryFunction<TProps, TTheme> = IStyleFactoryFunctionRaw<TProps, TTheme> & { _keys: (keyof TProps)[] };
/**
* An entry can be an individual operation, an array of operations, or a token function
*/
export type IStyleFactoryEntry<TProps, TTheme> =
| IStyleFactoryOperation<TProps, TTheme>
| IOperationSet<TProps, TTheme>
| IStyleFactoryFunction<TProps, TTheme>;
/**
* For a given slot a component author specifies an array of operations and functions to execute to produce props and styles
*/
export interface ISlotStyleFactories<TProps, TTheme> {
styleFactories?: (IOperationSet<TProps, TTheme> | IStyleFactoryFunction<TProps, TTheme>)[];
styleFactories?: IStyleFactoryEntry<TProps, TTheme> | IStyleFactoryEntry<TProps, TTheme>[];
}
/**