Skip to content

Commit

Permalink
feat(admin-ui): Add more native React UI components
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley committed Sep 18, 2023
1 parent 5da18f2 commit 04e03f8
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@import 'variables';

:host {
:host, .vdr-action-bar {
display: flex;
justify-content: space-between;
align-items: baseline;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { DataTable2SearchComponent } from './data-table-search.component';
* extend the {@link BaseListComponent} or {@link TypedBaseListComponent} class.
*
* @example
* ```HTML
* ```html
* <vdr-data-table-2
* id="product-review-list"
* [items]="items$ | async"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
}

::ng-deep .input-row {
input,
input:not([type='checkbox']),
select,
textarea,
vdr-zone-selector,
Expand All @@ -32,3 +32,11 @@
width: 100%;
}
}

.input-row {
input:not([type='checkbox']),
select,
textarea {
width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import "variables";


:host {
:host, .vdr-page-detail-layout {
display: grid;
grid-template-columns: 3fr 1fr;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,13 @@
@import "../../core/src/shared/components/card/card.component";
@import '../../core/src/shared/components/card/card.component';
@import '../../core/src/shared/components/form-field/form-field.component';
@import '../../core/src/shared/components/action-bar/action-bar.component.scss';
@import '../../core/src/shared/components/page-detail-layout/page-detail-layout.component';

.form-group .input-row.invalid {
input:not([type='checkbox']):not([type='radio']),
select,
textarea {
color: var(--color-error-700);
border-color: var(--color-error-300);
}
}
5 changes: 5 additions & 0 deletions packages/admin-ui/src/lib/react/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ export * from './components/react-custom-detail.component';
export * from './components/react-form-input.component';
export * from './components/react-route.component';
export * from './directives/react-component-host.directive';
export * from './react-components/ActionBar';
export * from './react-components/Card';
export * from './react-components/CdsIcon';
export * from './react-components/FormField';
export * from './react-components/Link';
export * from './react-components/PageBlock';
export * from './react-components/PageDetailLayout';
export * from './react-hooks/use-detail-component-data';
export * from './react-hooks/use-form-control';
export * from './react-hooks/use-injector';
Expand Down
29 changes: 29 additions & 0 deletions packages/admin-ui/src/lib/react/src/react-components/ActionBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { PropsWithChildren, ReactNode } from 'react';

/**
* @description
* A container for the primary actions on a list or detail page
*
* @example
* ```ts
* import { ActionBar } from '@vendure/admin-ui/react';
*
* export function MyComponent() {
* return (
* <ActionBar leftContent={<div>Optional left content</div>}>
* <button className='button primary'>Primary action</button>
* </ActionBar>
* );
* }
* ```
*
* @docsCategory react-components
*/
export function ActionBar(props: PropsWithChildren<{ leftContent?: ReactNode }>) {
return (
<div className={'vdr-action-bar'}>
<div className="left-content">{props.leftContent}</div>
<div className="right-content">{props.children}</div>
</div>
);
}
53 changes: 53 additions & 0 deletions packages/admin-ui/src/lib/react/src/react-components/CdsIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { ClarityIcons } from '@cds/core/icon';
import { IconShapeTuple } from '@cds/core/icon/interfaces/icon.interfaces';
import React, { DOMAttributes, useEffect } from 'react';

type CustomElement<T> = Partial<T & DOMAttributes<T> & { children: any }>;

export interface CdsIconProps {
shape: string;
size: string | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
direction: 'up' | 'down' | 'left' | 'right';
flip: 'horizontal' | 'vertical';
solid: boolean;
status: 'info' | 'success' | 'warning' | 'danger';
inverse: boolean;
badge: 'info' | 'success' | 'warning' | 'danger';
}

declare global {
namespace JSX {
interface IntrinsicElements {
['cds-icon']: CustomElement<CdsIconProps>;
}
}
}

export function registerCdsIcon(icon: IconShapeTuple) {
ClarityIcons.addIcons(icon);
}

/**
* @description
* A React wrapper for the Clarity UI icon component.
*
* @example
* ```ts
* import { userIcon } from '@cds/core/icon';
* import { CdsIcon } from '@vendure/admin-ui/react';
*
* registerCdsIcon(userIcon);
* export function MyComponent() {
* return <CdsIcon icon={userIcon} badge="warning" solid size="lg"></CdsIcon>;
* }
* ```
*
* @docsCategory react-components
*/
export function CdsIcon(props: { icon: IconShapeTuple; className?: string } & Partial<CdsIconProps>) {
const { icon, ...rest } = props;
useEffect(() => {
ClarityIcons.addIcons(icon);
}, [icon]);
return <cds-icon {...rest} shape={icon[0]}></cds-icon>;
}
41 changes: 41 additions & 0 deletions packages/admin-ui/src/lib/react/src/react-components/FormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { PropsWithChildren } from 'react';

/**
* @description
* A wrapper around form fields which provides a label, tooltip and error message.
*
* @example
* ```ts
* import { FormField } from '@vendure/admin-ui/react';
*
* export function MyReactComponent() {
* return (
* <FormField label="My field" tooltip="This is a tooltip" invalid errorMessage="This field is invalid">
* <input type="text" />
* </FormField>
* );
* }
* ```
*
* @docsCategory react-components
*/
export function FormField(
props: PropsWithChildren<{
for?: string;
label?: string;
tooltip?: string;
invalid?: boolean;
errorMessage?: string;
}>,
) {
return (
<div
className={`form-group ` + (!props.label ? 'no-label' : '') + (props.invalid ? 'clr-error' : '')}
>
{props.label && <label htmlFor={props.for ?? ''}>{props.label}</label>}
{props.tooltip && <div className="tooltip-text">{props.tooltip}</div>}
<div className={`input-row ` + (props.invalid ? 'invalid' : '')}>{props.children}</div>
{props.errorMessage && <div className="error-message">{props.errorMessage}</div>}
</div>
);
}
24 changes: 24 additions & 0 deletions packages/admin-ui/src/lib/react/src/react-components/PageBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { PropsWithChildren } from 'react';

/**
* @description
* A container for page content which provides a consistent width and spacing.
*
* @example
* ```ts
* import { PageBlock } from '@vendure/admin-ui/react';
*
* export function MyComponent() {
* return (
* <PageBlock>
* ...
* </PageBlock>
* );
* }
* ```
*
* @docsCategory react-components
*/
export function PageBlock(props: PropsWithChildren) {
return <div className="page-block">{props.children}</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { PropsWithChildren, ReactNode } from 'react';

/**
* @description
* A responsive container for detail views with a main content area and an optional sidebar.
*
* @example
* ```ts
* import { PageDetailLayout } from '@vendure/admin-ui/react';
*
* export function MyComponent() {
* return (
* <PageDetailLayout sidebar={<div>Sidebar content</div>}>
* <div>Main content</div>
* </PageDetailLayout>
* );
* }
* ```
*
* @docsCategory react-components
*/
export function PageDetailLayout(props: PropsWithChildren<{ sidebar?: ReactNode }>) {
return (
<div className={'vdr-page-detail-layout'}>
<div className="main">{props.children}</div>
<div className="sidebar">{props.sidebar}</div>
</div>
);
}

0 comments on commit 04e03f8

Please sign in to comment.