diff --git a/src/plugins/components/paragraph.ts b/src/plugins/components/paragraph/index.ts similarity index 97% rename from src/plugins/components/paragraph.ts rename to src/plugins/components/paragraph/index.ts index d8f08e3..34acafe 100644 --- a/src/plugins/components/paragraph.ts +++ b/src/plugins/components/paragraph/index.ts @@ -1,8 +1,8 @@ import plugin from 'tailwindcss/plugin' import { defu } from 'defu' -import { type PluginOption, defaultPluginOptions } from '../options' +import { type PluginOption, defaultPluginOptions } from '../../options' -const defaultParagraphConfig = { +export const defaultParagraphConfig = { textXS: 'xs', textSM: 'sm', textMD: 'base', diff --git a/src/plugins/components/paragraph/paragraph.component.ts b/src/plugins/components/paragraph/paragraph.component.ts new file mode 100644 index 0000000..3b16d5e --- /dev/null +++ b/src/plugins/components/paragraph/paragraph.component.ts @@ -0,0 +1,29 @@ +import { html, unsafeStatic } from 'lit/static-html.js' +import { spread } from '@open-wc/lit-helpers' + +import type { ParagraphAttrs } from './paragraph.types' +import * as variants from './paragraph.variants' + +/** + * Primary UI component for user interaction + */ +export const Paragraph = ({ + size = 'md', + weight = 'normal', + lead = 'normal', + as = 'p', + children, + ...attrs +}: ParagraphAttrs) => { + return html` + <${unsafeStatic(as)} class=${[ + 'nui-paragraph', + size && variants.size[size], + weight && variants.weight[weight], + lead && variants.lead[lead], + ] + .filter(Boolean) + .join(' ')} + ${spread(attrs)}>${children} + ` +} diff --git a/src/plugins/components/paragraph/paragraph.doc.mdx b/src/plugins/components/paragraph/paragraph.doc.mdx new file mode 100644 index 0000000..5f4529c --- /dev/null +++ b/src/plugins/components/paragraph/paragraph.doc.mdx @@ -0,0 +1,75 @@ +import { Meta, Primary, Controls, Story } from '@storybook/blocks' +import * as ParagraphStories from './paragraph.stories' +import { defaultParagraphConfig } from '.' + + + +# Paragraph + +Paragraphs are an important part of any website or application and are often referred as part of the "Typography". They help creating structure and consistency accross the page and your content. + + + +## Props + + + +## Variants + +
+ +### Sizes + +Use the Paragraph component to display some content or a subtitle. You can use various props to customize the size, weight, the line-height and the Html tag used to render the paragraph. + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +## Customization + +### Default config + +
+
+ + View configuration options + + + + + +
+
+        {JSON.stringify(defaultParagraphConfig, null, 2)}
+      
+
+ +
+
\ No newline at end of file diff --git a/src/plugins/components/paragraph/paragraph.stories.ts b/src/plugins/components/paragraph/paragraph.stories.ts new file mode 100644 index 0000000..661faeb --- /dev/null +++ b/src/plugins/components/paragraph/paragraph.stories.ts @@ -0,0 +1,149 @@ +import type { Meta, StoryObj } from '@storybook/web-components' +import { html } from 'lit' + +import type { ParagraphAttrs } from './paragraph.types' +import { Paragraph } from './paragraph.component' + +// More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction +const meta = { + title: 'Shuriken UI/Typography/Paragraph', + // tags: ['autodocs'], + render: (args) => Paragraph(args), + argTypes: { + as: { + control: { type: 'select' }, + options: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'span', 'p'], + defaultValue: 'p', + }, + size: { + control: { type: 'select' }, + options: [ + 'xs', + 'sm', + 'md', + 'lg', + 'xl', + '2xl', + '3xl', + '4xl', + '5xl', + '6xl', + '7xl', + '8xl', + '9xl', + ], + defaultValue: 'md', + }, + weight: { + control: { type: 'select' }, + options: ['light', 'normal', 'medium', 'semibold', 'bold', 'extrabold'], + defaultValue: 'normal', + }, + lead: { + control: { type: 'select' }, + options: ['none', 'tight', 'snug', 'normal', 'relaxed', 'loose'], + defaultValue: 'normal', + }, + }, +} satisfies Meta + +export default meta +type Story = StoryObj + +// first export is the Primary story + +// #region Main +export const Main: Story = { + name: 'Main example', + args: { + // set default values used for UI preview + as: 'p', + size: 'md', + weight: 'normal', + lead: 'none', + children: html` + This is a nice content paragraph. + `, + }, +} +// #endregion + +// #region Variants +export const Size2Xl: Story = { + name: 'Size: 2xl', + args: { + as: 'p', + size: '2xl', + weight: 'normal', + lead: 'none', + children: html` + Iam a 2xl paragraph + `, + }, +} + +export const SizeXl: Story = { + name: 'Size: xl', + args: { + as: 'p', + size: 'xl', + weight: 'normal', + lead: 'none', + children: html` + Iam a xl paragraph + `, + }, +} + +export const SizeLg: Story = { + name: 'Size: lg', + args: { + as: 'p', + size: 'lg', + weight: 'normal', + lead: 'none', + children: html` + Iam a lg paragraph + `, + }, +} + +export const SizeMd: Story = { + name: 'Size: md', + args: { + as: 'p', + size: 'md', + weight: 'normal', + lead: 'none', + children: html` + Iam a md paragraph + `, + }, +} + +export const SizeSm: Story = { + name: 'Size: sm', + args: { + as: 'p', + size: 'sm', + weight: 'normal', + lead: 'none', + children: html` + Iam a sm paragraph + `, + }, +} + +export const SizeXs: Story = { + name: 'Size: xs', + args: { + as: 'p', + size: 'xs', + weight: 'normal', + lead: 'none', + children: html` + Iam a sm paragraph + `, + }, +} +// #endregion diff --git a/src/plugins/components/paragraph/paragraph.test.ts b/src/plugins/components/paragraph/paragraph.test.ts new file mode 100644 index 0000000..d47e61d --- /dev/null +++ b/src/plugins/components/paragraph/paragraph.test.ts @@ -0,0 +1,35 @@ +import { axe } from 'vitest-axe' +import { expect, test, describe } from 'vitest' +import { render, html } from 'lit' + +import { Paragraph } from './paragraph.component' + +describe('Paragraph', () => { + test('Should render slot', () => { + const paragraph = Paragraph({ + children: html` + Hello world + `, + }) + + render(paragraph, document.body) + + expect(document.body.querySelector('.nui-paragraph')?.outerHTML)?.toContain( + 'Hello world', + ) + }) + + test('Should have no axe violations', async () => { + const paragraph = Paragraph({ + children: html` + Hello world + `, + }) + + render(paragraph, document.body) + + expect( + await axe(document.body.querySelector('.nui-paragraph')!.outerHTML), + )?.toHaveNoViolations() + }) +}) diff --git a/src/plugins/components/paragraph/paragraph.types.ts b/src/plugins/components/paragraph/paragraph.types.ts new file mode 100644 index 0000000..9c79eaf --- /dev/null +++ b/src/plugins/components/paragraph/paragraph.types.ts @@ -0,0 +1,30 @@ +import type { PropertyVariant } from '~/types/utils' + +export interface ParagraphProps extends Record { + as?: string + size?: + | 'xs' + | 'sm' + | 'md' + | 'lg' + | 'xl' + | '2xl' + | '3xl' + | '4xl' + | '5xl' + | '6xl' + | '7xl' + | '8xl' + | '9xl' + weight?: 'light' | 'normal' | 'medium' | 'semibold' | 'bold' | 'extrabold' + lead?: 'none' | 'tight' | 'snug' | 'normal' | 'relaxed' | 'loose' +} + +export interface ParagraphEvents {} + +export interface ParagraphSlots { + children: any +} + +export type ParagraphAttrs = ParagraphProps & ParagraphEvents & ParagraphSlots +export type ParagraphVariant = PropertyVariant diff --git a/src/plugins/components/paragraph/paragraph.variants.ts b/src/plugins/components/paragraph/paragraph.variants.ts new file mode 100644 index 0000000..72a4b5e --- /dev/null +++ b/src/plugins/components/paragraph/paragraph.variants.ts @@ -0,0 +1,35 @@ +import type { ParagraphVariant } from './paragraph.types' + +export const size = { + xs: 'nui-paragraph-xs', + sm: 'nui-paragraph-sm', + md: 'nui-paragraph-md', + lg: 'nui-paragraph-lg', + xl: 'nui-paragraph-xl', + '2xl': 'nui-paragraph-2xl', + '3xl': 'nui-paragraph-3xl', + '4xl': 'nui-paragraph-4xl', + '5xl': 'nui-paragraph-5xl', + '6xl': 'nui-paragraph-6xl', + '7xl': 'nui-paragraph-7xl', + '8xl': 'nui-paragraph-8xl', + '9xl': 'nui-paragraph-9xl', +} as const satisfies ParagraphVariant<'size'> + +export const weight = { + light: 'nui-weight-light', + normal: 'nui-weight-normal', + medium: 'nui-weight-medium', + semibold: 'nui-weight-semibold', + bold: 'nui-weight-bold', + extrabold: 'nui-weight-extrabold', +} as const satisfies ParagraphVariant<'weight'> + +export const lead = { + none: 'nui-lead-none', + tight: 'nui-lead-tight', + snug: 'nui-lead-snug', + normal: 'nui-lead-normal', + relaxed: 'nui-lead-relaxed', + loose: 'nui-lead-loose', +} as const satisfies ParagraphVariant<'lead'>