Use size class (#28)

* Add use-size-class composition

* Use size-class composition in components

* Remove unnecessary tests

* Update readme
This commit is contained in:
Rijk van Zanten
2020-02-13 16:37:58 -05:00
committed by GitHub
parent 0b1f73fc8e
commit d63396eb25
13 changed files with 179 additions and 381 deletions

View File

@@ -16,46 +16,4 @@ describe('Avatar', () => {
await component.vm.$nextTick();
expect(component.classes()).toContain('tile');
});
describe('Sizes', () => {
test('Extra Small', () => {
component.setProps({
xSmall: true,
small: false,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-small'));
});
test('Small', () => {
component.setProps({
xSmall: false,
small: true,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('small'));
});
test('Large', () => {
component.setProps({
xSmall: false,
small: false,
large: true,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('large'));
});
test('Extra Large', () => {
component.setProps({
xSmall: false,
small: false,
large: false,
xLarge: true
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-large'));
});
});
});

View File

@@ -7,6 +7,7 @@
<script lang="ts">
import { createComponent, computed } from '@vue/composition-api';
import parseCSSVar from '../../utils/parse-css-var';
import useSizeClass, { sizeProps } from '@/compositions/size-class';
export default createComponent({
props: {
@@ -18,32 +19,10 @@ export default createComponent({
type: Boolean,
default: false
},
xSmall: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
},
xLarge: {
type: Boolean,
default: false
}
...sizeProps
},
setup(props) {
const sizeClass = computed<string | null>(() => {
if (props.xSmall) return 'x-small';
if (props.small) return 'small';
if (props.large) return 'large';
if (props.xLarge) return 'x-large';
return null;
});
const sizeClass = useSizeClass(props);
return { sizeClass };
}
});

View File

@@ -74,53 +74,4 @@ describe('Button', () => {
expect(component.classes()).toContain('loading');
});
describe('Sizes', () => {
const component = mount(VButton, {
localVue,
propsData: {
name: 'person'
}
});
test('Extra Small', () => {
component.setProps({
xSmall: true,
small: false,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-small'));
});
test('Small', () => {
component.setProps({
xSmall: false,
small: true,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('small'));
});
test('Large', () => {
component.setProps({
xSmall: false,
small: false,
large: true,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('large'));
});
test('Extra Large', () => {
component.setProps({
xSmall: false,
small: false,
large: false,
xLarge: true
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-large'));
});
});
});

View File

@@ -18,6 +18,7 @@
<script lang="ts">
import { createComponent, reactive, computed, Ref } from '@vue/composition-api';
import parseCSSVar from '@/utils/parse-css-var';
import useSizeClass, { sizeProps } from '@/compositions/size-class';
export default createComponent({
props: {
@@ -49,32 +50,10 @@ export default createComponent({
type: Boolean,
default: false
},
xSmall: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
},
xLarge: {
type: Boolean,
default: false
}
...sizeProps
},
setup(props) {
const sizeClass = computed<string | null>(() => {
if (props.xSmall) return 'x-small';
if (props.small) return 'small';
if (props.large) return 'large';
if (props.xLarge) return 'x-large';
return null;
});
const sizeClass = useSizeClass(props);
return { sizeClass };
}
});

View File

@@ -112,54 +112,4 @@ describe('Chip', () => {
expect(component.emitted('click')).toBe(undefined);
});
describe('Sizes', () => {
const component = mount(VChip, {
localVue,
propsData: {
color: '--blue-grey',
name: 'person'
}
});
test('Extra Small', () => {
component.setProps({
xSmall: true,
small: false,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-small'));
});
test('Small', () => {
component.setProps({
xSmall: false,
small: true,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('small'));
});
test('Large', () => {
component.setProps({
xSmall: false,
small: false,
large: true,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('large'));
});
test('Extra Large', () => {
component.setProps({
xSmall: false,
small: false,
large: false,
xLarge: true
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-large'));
});
});
});

View File

@@ -22,6 +22,7 @@
<script lang="ts">
import { createComponent, ref, computed } from '@vue/composition-api';
import parseCSSVar from '@/utils/parse-css-var';
import useSizeClass, { sizeProps } from '@/compositions/size-class';
export default createComponent({
props: {
@@ -49,22 +50,7 @@ export default createComponent({
type: Boolean,
default: false
},
xSmall: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
},
xLarge: {
type: Boolean,
default: false
}
...sizeProps
},
setup(props, { emit }) {
const _localActive = ref(true);
@@ -80,13 +66,7 @@ export default createComponent({
}
});
const sizeClass = computed<string | null>(() => {
if (props.xSmall) return 'x-small';
if (props.small) return 'small';
if (props.large) return 'large';
if (props.xLarge) return 'x-large';
return null;
});
const sizeClass = useSizeClass(props);
return { sizeClass, _active, onClick, onCloseClick };

View File

@@ -23,78 +23,16 @@ describe('Icon', () => {
expect(component.contains('svg')).toBe(true);
});
describe('Sizes', () => {
test('Superscript', async () => {
component.setProps({
sup: true,
xSmall: false,
small: false,
large: false,
xLarge: false
});
await component.vm.$nextTick();
expect(component.classes()).toContain('sup');
});
test('Extra Small', async () => {
component.setProps({
sup: false,
xSmall: true,
small: false,
large: false,
xLarge: false
});
await component.vm.$nextTick();
expect(component.classes()).toContain('x-small');
});
test('Small', async () => {
component.setProps({
sup: false,
xSmall: false,
small: true,
large: false,
xLarge: false
});
await component.vm.$nextTick();
expect(component.classes()).toContain('small');
});
test('Large', async () => {
component.setProps({
sup: false,
xSmall: false,
small: false,
large: true,
xLarge: false
});
await component.vm.$nextTick();
expect(component.classes()).toContain('large');
});
test('Extra Large', async () => {
component.setProps({
sup: false,
xSmall: false,
small: false,
large: false,
xLarge: true
});
await component.vm.$nextTick();
expect(component.classes()).toContain('x-large');
});
it('Uses the smallest size prop provided (sup)', async () => {
component.setProps({
sup: true,
xSmall: false,
small: true,
large: false,
xLarge: true
});
await component.vm.$nextTick();
expect(component.classes()).toContain('sup');
it('Supports superscript size class', async () => {
component.setProps({
sup: true,
xSmall: false,
small: false,
large: false,
xLarge: false
});
await component.vm.$nextTick();
expect(component.classes()).toContain('sup');
});
it('Adds the has-click class if a click event is passed', async () => {

View File

@@ -14,6 +14,7 @@
import { createComponent, reactive, computed } from '@vue/composition-api';
import parseCSSVar from '@/utils/parse-css-var';
import CustomIconBox from './custom-icons/box.vue';
import useSizeClass, { sizeProps } from '@/compositions/size-class';
const customIcons: string[] = ['box'];
@@ -32,22 +33,6 @@ export default createComponent({
type: Boolean,
default: false
},
xSmall: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
},
xLarge: {
type: Boolean,
default: false
},
left: {
type: Boolean,
default: false
@@ -55,17 +40,14 @@ export default createComponent({
right: {
type: Boolean,
default: false
}
},
...sizeProps
},
setup(props, { emit, listeners }) {
const sizeClass = computed<string | null>(() => {
if (props.sup) return 'sup';
if (props.xSmall) return 'x-small';
if (props.small) return 'small';
if (props.large) return 'large';
if (props.xLarge) return 'x-large';
return null;
return useSizeClass(props).value;
});
const customIconName = computed<string | null>(() => {

View File

@@ -11,55 +11,7 @@ describe('Spinner', () => {
beforeEach(() => (component = mount(VSpinner, { localVue })));
describe('Sizes', () => {
test('Extra Small', () => {
component.setProps({
xSmall: true,
small: false,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-small'));
});
test('Small', () => {
component.setProps({
xSmall: false,
small: true,
large: false,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('small'));
});
test('Large', () => {
component.setProps({
xSmall: false,
small: false,
large: true,
xLarge: false
});
component.vm.$nextTick(() => expect(component.classes()).toContain('large'));
});
test('Extra Large', () => {
component.setProps({
xSmall: false,
small: false,
large: false,
xLarge: true
});
component.vm.$nextTick(() => expect(component.classes()).toContain('x-large'));
});
it('Uses the smallest size prop provided (small)', () => {
component.setProps({
xSmall: false,
small: true,
large: false,
xLarge: true
});
component.vm.$nextTick(() => expect(component.classes()).toContain('small'));
});
it('Renders', () => {
expect(component.exists()).toBe(true);
});
});

View File

@@ -5,35 +5,12 @@
<script lang="ts">
import { createComponent, computed } from '@vue/composition-api';
import parseCSSVar from '@/utils/parse-css-var';
import useSizeClass, { sizeProps } from '@/compositions/size-class';
export default createComponent({
props: {
xSmall: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
},
xLarge: {
type: Boolean,
default: false
}
},
props: sizeProps,
setup(props) {
const sizeClass = computed<string | null>(() => {
if (props.xSmall) return 'x-small';
if (props.small) return 'small';
if (props.large) return 'large';
if (props.xLarge) return 'x-large';
return null;
});
const sizeClass = useSizeClass(props);
return { sizeClass };
}
});

View File

@@ -5,6 +5,7 @@ Compositions are reusable pieces of logic that can be used inside Vue components
## Table of Contents
* [Event Listener](#event-listener)
* [Size Class](#size-class)
* [Time from Now](#time-from-now)
* [Window Size](#window-size)
@@ -33,6 +34,29 @@ export default createComponent({
});
```
---
## Size Class
Shared size class prop handler for base components. Adds `x-small`, `small`, `large`, and `x-large` props to the component, and converts the prop into a string that can be added to classes.
### Usage
```js
import { createComponent } from '@vue/composition-api';
import useSizeClass, { sizeProps } from '@/compositions/size-class';
export default createComponent({
props: {
...sizeProps
},
setup(props) {
const sizeClass = useSizeClass(props);
}
});
```
---
## Time from Now
@@ -54,6 +78,8 @@ export default createComponent({
The composition accepts an optional second parameter that controls how often the value is update. You can set this to `0` if you don't want the value to update at all.
---
## Window Size
Returns a `ref` of `width` and `height` of the current window size. Updates the value on window resizes.

View File

@@ -0,0 +1,87 @@
import VueCompositionAPI, { Ref } from '@vue/composition-api';
import useSizeClass from './size-class';
import mountComposition from '../../.jest/mount-composition';
describe('Compositions / Size Class', () => {
it('Extracts the correct class based on given props', () => {
let props = {
xSmall: false,
small: false,
large: false,
xLarge: false,
ignoredKey: 'test'
};
mountComposition(() => {
const className = useSizeClass(props);
expect(className.value).toBe(null);
}).destroy();
props = {
xSmall: true,
small: false,
large: false,
xLarge: false,
ignoredKey: 'test'
};
mountComposition(() => {
const className = useSizeClass(props);
expect(className.value).toBe('x-small');
}).destroy();
props = {
xSmall: false,
small: true,
large: false,
xLarge: false,
ignoredKey: 'test'
};
mountComposition(() => {
const className = useSizeClass(props);
expect(className.value).toBe('small');
}).destroy();
props = {
xSmall: false,
small: false,
large: true,
xLarge: false,
ignoredKey: 'test'
};
mountComposition(() => {
const className = useSizeClass(props);
expect(className.value).toBe('large');
}).destroy();
props = {
xSmall: false,
small: false,
large: false,
xLarge: true,
ignoredKey: 'test'
};
mountComposition(() => {
const className = useSizeClass(props);
expect(className.value).toBe('x-large');
}).destroy();
});
it('Defaults to the smallest size if multiple sizes are passed', () => {
const props = {
xSmall: false,
small: true,
large: true,
xLarge: true,
ignoredKey: 'test'
};
mountComposition(() => {
const className = useSizeClass(props);
expect(className.value).toBe('small');
}).destroy();
});
});

View File

@@ -0,0 +1,39 @@
import { computed } from '@vue/composition-api';
export const sizeProps = {
xSmall: {
type: Boolean,
default: false
},
small: {
type: Boolean,
default: false
},
large: {
type: Boolean,
default: false
},
xLarge: {
type: Boolean,
default: false
}
};
interface RequiredProps {
xSmall: boolean;
small: boolean;
large: boolean;
xLarge: boolean;
}
export default function useSizeClass<T>(props: T & RequiredProps) {
const sizeClass = computed<string | null>(() => {
if (props.xSmall) return 'x-small';
if (props.small) return 'small';
if (props.large) return 'large';
if (props.xLarge) return 'x-large';
return null;
});
return sizeClass;
}