Skip to content

Commit

Permalink
[theme] Convert transitions from object to ES module (mui#22517)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon authored Sep 7, 2020
1 parent 1afb131 commit 3b5302b
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 109 deletions.
4 changes: 2 additions & 2 deletions packages/material-ui/src/styles/createMuiTheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import createTypography from './createTypography';
import shadows from './shadows';
import shape from './shape';
import createSpacing from './createSpacing';
import transitions from './transitions';
import { duration, easing, create, getAutoHeightDuration } from './transitions';
import zIndex from './zIndex';

function createMuiTheme(options = {}, ...args) {
Expand Down Expand Up @@ -34,7 +34,7 @@ function createMuiTheme(options = {}, ...args) {
typography: createTypography(palette, typographyInput),
spacing,
shape,
transitions,
transitions: { duration, easing, create, getAutoHeightDuration },
zIndex,
},
other,
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/styles/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export { TypographyStyle, Variant as TypographyVariant } from './createTypograph
export { default as makeStyles } from './makeStyles';
export { default as responsiveFontSizes } from './responsiveFontSizes';
export { ComponentsPropsList } from './props';
export * from './transitions';
export { Duration, Easing, Transitions, TransitionsOptions, duration, easing } from './transitions';
export { default as useTheme } from './useTheme';
export {
default as withStyles,
Expand Down
2 changes: 1 addition & 1 deletion packages/material-ui/src/styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export { default as createStyles } from './createStyles';
export { default as makeStyles } from './makeStyles';
export { default as responsiveFontSizes } from './responsiveFontSizes';
export { default as styled } from './styled';
export * from './transitions';
export { duration, easing } from './transitions';
export { default as useTheme } from './useTheme';
export { default as withStyles } from './withStyles';
export { default as withTheme } from './withTheme';
Expand Down
38 changes: 23 additions & 15 deletions packages/material-ui/src/styles/transitions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ export interface Duration {
}
export const duration: Duration;

export function formatMs(milliseconds: number): string;

export interface Transitions {
easing: Easing;
duration: Duration;
create(
props: string | string[],
options?: Partial<{ duration: number | string; easing: string; delay: number | string }>
): string;
getAutoHeightDuration(height: number): number;
}

export interface TransitionsOptions {
easing?: Partial<Easing>;
duration?: Partial<Duration>;
Expand All @@ -39,7 +27,27 @@ export interface TransitionsOptions {
getAutoHeightDuration?: (height: number) => number;
}

// export type TransitionsOptions = DeepPartial<Transitions>;
/**
* @private
*
* @param props
* @param options
*/
export function create(
props: string | string[],
options?: Partial<{ duration: number | string; easing: string; delay: number | string }>
): string;

/**
* @private
*
* @param height
*/
export function getAutoHeightDuration(height: number): number;

declare const transitions: Transitions;
export default transitions;
export interface Transitions {
easing: Easing;
duration: Duration;
create: typeof create;
getAutoHeightDuration: typeof getAutoHeightDuration;
}
105 changes: 47 additions & 58 deletions packages/material-ui/src/styles/transitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,70 +32,59 @@ function formatMs(milliseconds) {
return `${Math.round(milliseconds)}ms`;
}

/**
* @param {string|Array} props
* @param {object} param
* @param {string} param.prop
* @param {number} param.duration
* @param {string} param.easing
* @param {number} param.delay
*/
export default {
easing,
duration,
create: (props = ['all'], options = {}) => {
const {
duration: durationOption = duration.standard,
easing: easingOption = easing.easeInOut,
delay = 0,
...other
} = options;
export function create(props = ['all'], options = {}) {
const {
duration: durationOption = duration.standard,
easing: easingOption = easing.easeInOut,
delay = 0,
...other
} = options;

if (process.env.NODE_ENV !== 'production') {
const isString = (value) => typeof value === 'string';
// IE 11 support, replace with Number.isNaN
// eslint-disable-next-line no-restricted-globals
const isNumber = (value) => !isNaN(parseFloat(value));
if (!isString(props) && !Array.isArray(props)) {
console.error('Material-UI: Argument "props" must be a string or Array.');
}

if (!isNumber(durationOption) && !isString(durationOption)) {
console.error(
`Material-UI: Argument "duration" must be a number or a string but found ${durationOption}.`,
);
}
if (process.env.NODE_ENV !== 'production') {
const isString = (value) => typeof value === 'string';
// IE 11 support, replace with Number.isNaN
// eslint-disable-next-line no-restricted-globals
const isNumber = (value) => !isNaN(parseFloat(value));
if (!isString(props) && !Array.isArray(props)) {
console.error('Material-UI: Argument "props" must be a string or Array.');
}

if (!isString(easingOption)) {
console.error('Material-UI: Argument "easing" must be a string.');
}
if (!isNumber(durationOption) && !isString(durationOption)) {
console.error(
`Material-UI: Argument "duration" must be a number or a string but found ${durationOption}.`,
);
}

if (!isNumber(delay) && !isString(delay)) {
console.error('Material-UI: Argument "delay" must be a number or a string.');
}
if (!isString(easingOption)) {
console.error('Material-UI: Argument "easing" must be a string.');
}

if (Object.keys(other).length !== 0) {
console.error(`Material-UI: Unrecognized argument(s) [${Object.keys(other).join(',')}].`);
}
if (!isNumber(delay) && !isString(delay)) {
console.error('Material-UI: Argument "delay" must be a number or a string.');
}

return (Array.isArray(props) ? props : [props])
.map(
(animatedProp) =>
`${animatedProp} ${
typeof durationOption === 'string' ? durationOption : formatMs(durationOption)
} ${easingOption} ${typeof delay === 'string' ? delay : formatMs(delay)}`,
)
.join(',');
},
getAutoHeightDuration(height) {
if (!height) {
return 0;
if (Object.keys(other).length !== 0) {
console.error(`Material-UI: Unrecognized argument(s) [${Object.keys(other).join(',')}].`);
}
}

const constant = height / 36;
return (Array.isArray(props) ? props : [props])
.map(
(animatedProp) =>
`${animatedProp} ${
typeof durationOption === 'string' ? durationOption : formatMs(durationOption)
} ${easingOption} ${typeof delay === 'string' ? delay : formatMs(delay)}`,
)
.join(',');
}

// https://www.wolframalpha.com/input/?i=(4+%2B+15+*+(x+%2F+36+)+**+0.25+%2B+(x+%2F+36)+%2F+5)+*+10
return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);
},
};
export function getAutoHeightDuration(height) {
if (!height) {
return 0;
}

const constant = height / 36;

// https://www.wolframalpha.com/input/?i=(4+%2B+15+*+(x+%2F+36+)+**+0.25+%2B+(x+%2F+36)+%2F+5)+*+10
return Math.round((4 + 15 * constant ** 0.25 + constant / 5) * 10);
}
64 changes: 32 additions & 32 deletions packages/material-ui/src/styles/transitions.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { stub } from 'sinon';
import transitions, { easing, duration } from './transitions';
import { create, easing, duration, getAutoHeightDuration } from './transitions';

describe('transitions', () => {
describe('create() function', () => {
Expand All @@ -16,146 +16,146 @@ describe('transitions', () => {
});

it('should warn when first argument is of bad type', () => {
transitions.create(5554);
create(5554);
expect(consoleErrorStub.args[0][0]).to.include(
'Material-UI: Argument "props" must be a string or Array',
);
transitions.create({});
create({});
expect(consoleErrorStub.args[1][0]).to.include(
'Material-UI: Argument "props" must be a string or Array',
);
});

it('should warn when bad "duration" option type', () => {
transitions.create('font', { duration: null });
create('font', { duration: null });
expect(consoleErrorStub.args[0][0]).to.include(
'Material-UI: Argument "duration" must be a number or a string but found null',
);
transitions.create('font', { duration: {} });
create('font', { duration: {} });
expect(consoleErrorStub.args[1][0]).to.include(
'Material-UI: Argument "duration" must be a number or a string but found [object Object]',
);
});

it('should warn when bad "easing" option type', () => {
transitions.create('transform', { easing: 123 });
create('transform', { easing: 123 });
expect(consoleErrorStub.args[0][0]).to.include(
'Material-UI: Argument "easing" must be a string',
);
transitions.create('transform', { easing: {} });
create('transform', { easing: {} });
expect(consoleErrorStub.args[1][0]).to.include(
'Material-UI: Argument "easing" must be a string',
);
});

it('should warn when bad "delay" option type', () => {
transitions.create('size', { delay: null });
create('size', { delay: null });
expect(consoleErrorStub.args[0][0]).to.include(
'Material-UI: Argument "delay" must be a number or a string',
);
transitions.create('size', { delay: {} });
create('size', { delay: {} });
expect(consoleErrorStub.args[1][0]).to.include(
'Material-UI: Argument "delay" must be a number or a string',
);
});

it('should warn when passed unrecognized option', () => {
transitions.create('size', { fffds: 'value' });
create('size', { fffds: 'value' });
expect(consoleErrorStub.args[0][0]).to.include(
'Material-UI: Unrecognized argument(s) [fffds]',
);
});
});

it('should create default transition without arguments', () => {
const transition = transitions.create();
const transition = create();
expect(transition).to.equal(`all ${duration.standard}ms ${easing.easeInOut} 0ms`);
});

it('should take string props as a first argument', () => {
const transition = transitions.create('color');
const transition = create('color');
expect(transition).to.equal(`color ${duration.standard}ms ${easing.easeInOut} 0ms`);
});

it('should also take array of props as first argument', () => {
const options = { delay: 20 };
const multiple = transitions.create(['color', 'size'], options);
const single1 = transitions.create('color', options);
const single2 = transitions.create('size', options);
const multiple = create(['color', 'size'], options);
const single1 = create('color', options);
const single2 = create('size', options);
const expected = `${single1},${single2}`;
expect(multiple).to.equal(expected);
});

it('should optionally accept number "duration" option in second argument', () => {
const transition = transitions.create('font', { duration: 500 });
const transition = create('font', { duration: 500 });
expect(transition).to.equal(`font 500ms ${easing.easeInOut} 0ms`);
});

it('should optionally accept string "duration" option in second argument', () => {
const transition = transitions.create('font', { duration: '500ms' });
const transition = create('font', { duration: '500ms' });
expect(transition).to.equal(`font 500ms ${easing.easeInOut} 0ms`);
});

it('should round decimal digits of "duration" prop to whole numbers', () => {
const transition = transitions.create('font', { duration: 12.125 });
const transition = create('font', { duration: 12.125 });
expect(transition).to.equal(`font 12ms ${easing.easeInOut} 0ms`);
});

it('should optionally accept string "easing" option in second argument', () => {
const transition = transitions.create('transform', { easing: easing.sharp });
const transition = create('transform', { easing: easing.sharp });
expect(transition).to.equal(`transform ${duration.standard}ms ${easing.sharp} 0ms`);
});

it('should optionally accept number "delay" option in second argument', () => {
const transition = transitions.create('size', { delay: 150 });
const transition = create('size', { delay: 150 });
expect(transition).to.equal(`size ${duration.standard}ms ${easing.easeInOut} 150ms`);
});

it('should optionally accept string "delay" option in second argument', () => {
const transition = transitions.create('size', { delay: '150ms' });
const transition = create('size', { delay: '150ms' });
expect(transition).to.equal(`size ${duration.standard}ms ${easing.easeInOut} 150ms`);
});

it('should round decimal digits of "delay" prop to whole numbers', () => {
const transition = transitions.create('size', { delay: 1.547 });
const transition = create('size', { delay: 1.547 });
expect(transition).to.equal(`size ${duration.standard}ms ${easing.easeInOut} 2ms`);
});

it('should return zero when not passed arguments', () => {
const zeroHeightDuration = transitions.getAutoHeightDuration();
const zeroHeightDuration = getAutoHeightDuration();
expect(zeroHeightDuration).to.equal(0);
});

it('should return zero when passed undefined', () => {
const zeroHeightDuration = transitions.getAutoHeightDuration(undefined);
const zeroHeightDuration = getAutoHeightDuration(undefined);
expect(zeroHeightDuration).to.equal(0);
});

it('should return zero when passed null', () => {
const zeroHeightDuration = transitions.getAutoHeightDuration(null);
const zeroHeightDuration = getAutoHeightDuration(null);
expect(zeroHeightDuration).to.equal(0);
});

it('should return NaN when passed a negative number', () => {
const zeroHeightDurationNegativeOne = transitions.getAutoHeightDuration(-1);
const zeroHeightDurationNegativeOne = getAutoHeightDuration(-1);
// eslint-disable-next-line no-restricted-globals
expect(isNaN(zeroHeightDurationNegativeOne)).to.equal(true);
const zeroHeightDurationSmallNegative = transitions.getAutoHeightDuration(-0.000001);
const zeroHeightDurationSmallNegative = getAutoHeightDuration(-0.000001);
// eslint-disable-next-line no-restricted-globals
expect(isNaN(zeroHeightDurationSmallNegative)).to.equal(true);
const zeroHeightDurationBigNegative = transitions.getAutoHeightDuration(-100000);
const zeroHeightDurationBigNegative = getAutoHeightDuration(-100000);
// eslint-disable-next-line no-restricted-globals
expect(isNaN(zeroHeightDurationBigNegative)).to.equal(true);
});

it('should return values for pre-calculated positive examples', () => {
let zeroHeightDuration = transitions.getAutoHeightDuration(14);
let zeroHeightDuration = getAutoHeightDuration(14);
expect(zeroHeightDuration).to.equal(159);
zeroHeightDuration = transitions.getAutoHeightDuration(100);
zeroHeightDuration = getAutoHeightDuration(100);
expect(zeroHeightDuration).to.equal(239);
zeroHeightDuration = transitions.getAutoHeightDuration(0.0001);
zeroHeightDuration = getAutoHeightDuration(0.0001);
expect(zeroHeightDuration).to.equal(46);
zeroHeightDuration = transitions.getAutoHeightDuration(100000);
zeroHeightDuration = getAutoHeightDuration(100000);
expect(zeroHeightDuration).to.equal(6685);
});
});
Expand Down

0 comments on commit 3b5302b

Please sign in to comment.