Skip to content

Commit

Permalink
[CHUX-457] Create letter-icon component (#1945)
Browse files Browse the repository at this point in the history
* [CHUX-457] add component and story

* [CHUX-457] add unit tests and snapshot

* [CHUX-457] add image snapshots

* [CHUX-457] change text color on hover

* [CHUX-457] design feedback

* 2.8.4

* [CHUX-457] revert version bump

* [CHUX-457] use button element if icon is clickable

* [CHUX-457] always display uppercase letter

* [CHUX-457] replace svg

* [CHUX-457] coverage

* [CHUX-457] remove leftover styles

* [CHUX-457] computed refactor

* [CHUX-457] size enum

* [CHUX-457] hide icon if text prop is empty
  • Loading branch information
raulwwq0 authored May 8, 2024
1 parent d63b781 commit 2245f65
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`EcLetterIcon > should render as expected 1`] = `
<div
class="ec-letter-icon ec-letter-icon--sm"
data-test="ec-letter-icon"
>
<span
class="ec-letter-icon__text"
data-test="ec-letter-icon__text"
>
T
</span>
</div>
`;
73 changes: 73 additions & 0 deletions src/components/ec-letter-icon/ec-letter-icon.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { type ComponentMountingOptions, mount } from '@vue/test-utils';

import EcLetterIcon from './ec-letter-icon.vue';
import { type LetterIconProps, LetterIconSize } from './types';

describe('EcLetterIcon', () => {
function mountLetterIcon(props: LetterIconProps, mountOpts?: ComponentMountingOptions<typeof EcLetterIcon>) {
return mount(
EcLetterIcon,
{
props: {
...props,
},
...mountOpts,
},
);
}

it('should render as expected', () => {
const wrapper = mountLetterIcon({
text: 'Test',
});
expect(wrapper.element).toMatchSnapshot();
});

it('should render the first letter of the text we passed through prop', () => {
const wrapper = mountLetterIcon({
text: 'Test',
});

const text = wrapper.findByDataTest('ec-letter-icon__text');
expect(text.text()).toBe('T');
});

it('should render the letter as uppercase if the text begins with a lowercase character', () => {
const wrapper = mountLetterIcon({
text: 'test',
});

const text = wrapper.findByDataTest('ec-letter-icon__text');
expect(text.text()).toBe('T');
});

it('should not display the icon if text is an empty string', () => {
const wrapper = mountLetterIcon({
text: '',
});

const container = wrapper.findByDataTest('ec-letter-icon');
expect(container.exists()).toBe(false);
});

it('should apply corresponding class if "size" prop is set', () => {
const wrapper = mountLetterIcon({
text: 'Test',
size: LetterIconSize.MEDIUM,
});

const container = wrapper.findByDataTest('ec-letter-icon');
expect(container.classes()).toContain('ec-letter-icon--md');
});

it('should use button element and add clickable class if "isClickable" prop is set', () => {
const wrapper = mountLetterIcon({
text: 'Test',
isClickable: true,
});

const container = wrapper.findByDataTest('ec-letter-icon');
expect(container.element.tagName).toContain('BUTTON');
expect(container.classes()).toContain('ec-letter-icon--clickable');
});
});
40 changes: 40 additions & 0 deletions src/components/ec-letter-icon/ec-letter-icon.story.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Meta, StoryFn } from '@storybook/vue3';

import EcLetterIcon from './ec-letter-icon.vue';
import { LetterIconSize } from './types';

export default {
title: 'Letter Icon',
component: EcLetterIcon,
} as Meta<typeof EcLetterIcon>;

type EcLetterIconStory = StoryFn<typeof EcLetterIcon>;

const Template: EcLetterIconStory = args => ({
components: { EcLetterIcon },
setup() {
return {
args,
};
},
template: `
<div class="tw-p-24">
<ec-letter-icon
v-bind="args"
/>
</div>
`,
});

export const basic = Template.bind({});

basic.args = {
text: 'Letter Icon',
};

basic.argTypes = {
size: {
options: Object.values(LetterIconSize),
control: { type: 'select' },
},
};
66 changes: 66 additions & 0 deletions src/components/ec-letter-icon/ec-letter-icon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<template>
<component
:is="isClickable ? 'button' : 'div'"
v-if="text"
data-test="ec-letter-icon"
class="ec-letter-icon"
:class="{
'ec-letter-icon--clickable': isClickable,
[`ec-letter-icon--${size}`]: true,
}"
>
<span
data-test="ec-letter-icon__text"
class="ec-letter-icon__text"
>
{{ firstLetter }}
</span>
</component>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { type LetterIconProps, LetterIconSize } from './types';
const props = withDefaults(defineProps<LetterIconProps>(), {
size: LetterIconSize.SMALL,
isClickable: false,
});
const firstLetter = computed(() => (props.text[0]?.toUpperCase()));
</script>

<style>
.ec-letter-icon {
@apply tw-flex tw-justify-center tw-items-center tw-rounded-1/2 tw-bg-gray-6;
&__text {
@apply tw-select-none tw-text-gray-2;
}
&--sm {
@apply tw-size-24;
font-size: 14px;
}
&--md {
@apply tw-size-32;
font-size: 18px;
}
&--clickable {
@apply tw-cursor-pointer tw-border-0 tw-p-0;
&:hover {
@apply tw-bg-gray-2;
.ec-letter-icon__text {
@apply tw-text-gray-7;
}
}
}
}
</style>
1 change: 1 addition & 0 deletions src/components/ec-letter-icon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ec-letter-icon.vue';
10 changes: 10 additions & 0 deletions src/components/ec-letter-icon/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface LetterIconProps {
text: string,
size?: LetterIconSize,
isClickable?: boolean,
}

export enum LetterIconSize {
SMALL = 'sm',
MEDIUM = 'md',
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2245f65

Please sign in to comment.