@workgrid/ui
v0.0.9
Published
Workgrid UI component library
Downloads
4
Readme
Workgrid UI Components
What is Workgrid UI
Workgrid UI is a set of React UI components that wrap the Ionic UI framework used to build the Workgrid app and can also be adopted to build Workgrid microapps.
Installation & Usage
Installation
You can install Workgrid UI using a package manager like npm or yarn.
yarn add @workgrid/ui @ionic/react @ionic/react-router styled-components
Configuration
For the components to function correctly a set of CSS variables need to be defined to work correctly. Please see the Ionic documentation for details.
Usage
import { Button } from '@workgrid/ui'
const MyCustomComponent = () => (
<Button type="primary" onClick={() => {}}>
My Button
</Button>
)
A complete list of components can be found in our documentation here.
Developing Locally
Run Storybook
yarn storybook
Run tests
This project makes use of @storybook/addon-storyshots for accessibility and visual testing. In order for these tests to run storybook must be running.
yarn test
This project also uses Cypress for component level testing.
yarn test:cypress # Runs Cypress in CI mode
yarn cypress open-ct # Opens Cypress in interactive mode
Build
yarn build
Development Process
All code intended for production use must:
- Be introduced as a pull request targeting the
main
branch. - All pull requests must have a reference to the internal change management ticket by including it in the pull request title or description (e.g. ETSWORK-####)
- All code must be approved by at least two reviewers as well as any defined code owners in accordance with our internally documented code review guidelines
- At least one code reviewer should determine if the code change matches the intent of the change management ticket.
- All required checks must pass before a pull request can be merged. These are automated through GitHub Actions. Required build checks include:
- All automated unit tests need to pass
- All automated integration tests need to pass
- A successful distributable (e.g. package, binary) can be built
- Code security scanning checks need to pass
Contribution Guidelines
- Internationalization is out of scope for this library. All text that is displayed by a component must be provided via the API of the specific component
- We strive to align behavior to the platform that the user is using these components on
- We strive to implement the patterns and best practices documented here: React Typescript Cheatsheets
- For our application we strive to export entire "screens" as components. See "Screen Development" below
Screen Development
Due to limitations in Ionic Framework we have a specific approach to developing "screen" components that will be shown side-by-side on large devices such as tablets, desktop apps, or web applications. One example is SNNotificationDetailScren
. These screens shall export two components, one named Small
and one named Large
. The screens could then be consumed as follows:
import { CustomScreen } from '@workgrid/ui';
import Media from 'react-media';
import { IonPage, IonContent } from '@ionic/react'
export const AppScreen = () => {
return (
<Media queries={/* small screen */}>
{matches =>
return (<CustomScreen.Small />)
}
</Media>
<Media queries={/* large scren */}>
{matches => {
return (
<IonPage>
<IonContent>
<OtherCompoent />
<CustomScreen.Large />
<AnotherComponent />
</IonContent>
</IonPage>)
}}
</Media>
)
}
The Small
screen will have all the necessary Ionic components to support navigation and animation between screens.
export const Small = () => {
return (
<IonPage>
<IonHeader>
<CustomScreenHeaderHere />
</IonHeader>
<IonContent>
<CustomScreenContentHere />
</IonContent>
</IonPage>
)
}
The Large
screen will lack the Ionic components such as IonPage
, IonContent
and IonHeader
that would otherwise be used in mobile screens.
export const Large = () => {
return (
<>
<CustomScreenHeaderHere />
<CustomScreenContentHere />
</>
)
}
As shown above, the implementation is shared between Small
and Large
screens the only difference being that in one case we have wrapping Ion*
components and in the other, we don't. The components themselves should be designed so that they responsively support the needs of both large and small screens.
Testing
Cypress component testing shall be done for both Small
and Large
screens. An example pattern for re-use in a test suite could be as follows:
describe('CustomScreen', () => {
const screenTests = () => {
it('Use case to test', () => {
// Test here
})
}
describe('Small Screen Tests', () => {
beforeEach(() => {
mountIonApp(<CustomScreen.Small />)
})
screenTests()
})
describe('Large Screen Tests', () => {
beforeEach(() => {
mountIonApp(<CustomScreen.Large />)
})
screenTests()
})
})
Storybook Stories for both the Small
and Large
screens shall be created in the same .stories.tsx
file for the screen. For each story that is created, you must create one for both the Small
and Large
screens. An example pattern for re-use in a story is shown below:
import React from 'react'
import { Story, Meta } from '@storybook/react'
import { IonPage, IonContent } from '@ionic/react'
import * as CustomScreen from './CustomScreen'
import { CustomScreenProps } from './CustomScreen'
export default {
title: 'Screens/Custom Screen',
component: CustomScreen.Small
} as Meta
const SmallTemplate: Story<CustomScreenProps> = args => <CustomScreen.Small {...args} />
// Since we're using Small / Large screens in the same story we can't use the component decorators so we wrap the large screen in the required elements here
const LargeTemplate: Story<CustomScreenProps> = args => (
<IonPage>
<IonContent>
<CustomScreen.Large {...args} />
</IonContent>
</IonPage>
)
const UseCaseArgs = {
/* arguments here */
}
export const SmallUseCase = SmallTemplate.bind({})
SmallUseCase.args = UseCaseArgs
export const LargeUseCase = LargeTemplate.bind({})
LargeUseCase.args = UseCaseArgs