Skip to content

Commit

Permalink
feat: Add inline loading component (IBM#202)
Browse files Browse the repository at this point in the history
* Add inline loading component

Signed-off-by: Moiz Masud <[email protected]>

* Remove extra bracket from react export

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Update src/fragment-components/a-inline-loading.tsx

* Apply suggestions from code review

* Update src/fragment-components/a-inline-loading.tsx

* Add dropdown for status change and added accordian

Signed-off-by: Moiz Masud <[email protected]>

* Lint alittle

Signed-off-by: Moiz Masud <[email protected]>

Signed-off-by: Moiz Masud <[email protected]>
Co-authored-by: Akshat Patel <[email protected]>
Co-authored-by: Zvonimir Fras <[email protected]>
Signed-off-by: Connor-Ahearn <[email protected]>
  • Loading branch information
3 people authored and connorAhearn committed Jun 29, 2023
1 parent 2cbfbe7 commit 766732d
Show file tree
Hide file tree
Showing 5 changed files with 330 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/assets/component-icons/inline-loading.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
248 changes: 248 additions & 0 deletions src/fragment-components/a-inline-loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
import React from 'react';
import {
Accordion,
AccordionItem,
InlineLoading,
TextInput,
Dropdown,
NumberInput
} from 'carbon-components-react';
import { AComponent } from './a-component';
import { ComponentInfo } from '.';
import { css } from 'emotion';
import image from './../assets/component-icons/inline-loading.svg';
import {
nameStringToVariableString,
reactClassNamesFromComponentObj,
angularClassNamesFromComponentObj
} from '../utils/fragment-tools';

export const AInlineLoadingSettingsUI = ({ selectedComponent, setComponent }: any) => {
const statusItems = [
{ id: 'inactive', text: 'Inactive' },
{ id: 'active', text: 'Active' },
{ id: 'finished', text: 'Finished' },
{ id: 'error', text: 'Error' }
];
return <>
<Dropdown
id='combobox-status-dropdown'
label='Status'
titleText='Status'
items={statusItems}
selectedItem={statusItems.find(item => item.id === selectedComponent.status)}
itemToString={(item: any) => (item ? item.text : '')}
onChange={(event: any) => setComponent({
...selectedComponent,
status: event.selectedItem.id
})} />
<TextInput
value={selectedComponent.activeText}
labelText='Active text'
onChange={(event: any) => setComponent({
...selectedComponent,
activeText: event.currentTarget.value
})} />
<TextInput
value={selectedComponent.inactiveText}
labelText='Inactive text'
onChange={(event: any) => setComponent({
...selectedComponent,
inactiveText: event.currentTarget.value
})} />
<TextInput
value={selectedComponent.errorText}
labelText='Error text'
onChange={(event: any) => setComponent({
...selectedComponent,
errorText: event.currentTarget.value
})} />
<TextInput
value={selectedComponent.successText}
labelText='Success text'
onChange={(event: any) => setComponent({
...selectedComponent,
successText: event.currentTarget.value
})} />
<NumberInput
id='successDelay'
min={0}
label='Success delay (ms)'
name='successDelay'
value={selectedComponent.successDelay}
step={100}
onChange={(event: any) => setComponent({
...selectedComponent,
successDelay: Number(event.imaginaryTarget.value)
})} />
<Accordion align='start'>
<AccordionItem title='Icon descriptions'>
<TextInput
value={selectedComponent.activeIconDescription}
labelText='Active icon description'
onChange={(event: any) => setComponent({
...selectedComponent,
activeIconDescription: event.currentTarget.value
})} />
<TextInput
value={selectedComponent.errorIconDescription}
labelText='Error icon description'
onChange={(event: any) => setComponent({
...selectedComponent,
errorIconDescription: event.currentTarget.value
})} />
<TextInput
value={selectedComponent.finishedIconDescription}
labelText='Finished icon description'
onChange={(event: any) => setComponent({
...selectedComponent,
finishedIconDescription: event.currentTarget.value
})} />
<TextInput
value={selectedComponent.inactiveIconDescription}
labelText='Inactive icon description'
onChange={(event: any) => setComponent({
...selectedComponent,
inactiveIconDescription: event.currentTarget.value
})} />
</AccordionItem>
</Accordion>
</>;
};

export const AInlineLoadingCodeUI = ({ selectedComponent, setComponent }: any) => <TextInput
value={selectedComponent.codeContext?.name}
labelText='Input name'
onChange={(event: any) => setComponent({
...selectedComponent,
codeContext: {
...selectedComponent.codeContext,
name: event.currentTarget.value
}
})}
/>;

export const AInlineLoading = ({
componentObj,
...rest
}: any) => {
const status: any = {
active: {
iconDescription: componentObj.activeIconDescription,
description: componentObj.activeText
},
error: {
iconDescription: componentObj.errorIconDescription,
description: componentObj.errorText
},
inactive: {
iconDescription: componentObj.inactiveIconDescription,
description: componentObj.inactiveText
},
finished: {
iconDescription: componentObj.finishedIconDescription,
description: componentObj.successText
}
};

return (
<AComponent
componentObj={componentObj}
headingCss={css`display: block;`}
rejectDrop={true}
{...rest}>
<InlineLoading
successDelay={componentObj.successDelay}
description={status[componentObj.status].description}
iconDescription={status[componentObj.status].iconDescription}
status={componentObj.status}
className={componentObj.cssClasses?.map((cc: any) => cc.id).join(' ')} />
</AComponent>
);
};

export const componentInfo: ComponentInfo = {
component: AInlineLoading,
codeUI: AInlineLoadingCodeUI,
settingsUI: AInlineLoadingSettingsUI,
keywords: ['inline', 'loading'],
name: 'Inline loading',
type: 'inline-loading',
defaultComponentObj: {
type: 'inline-loading',
status: 'active',
activeText: 'Loading...',
successText: 'Finished.',
errorText: 'Error!'
},
image,
codeExport: {
angular: {
inputs: ({ json }) => `@Input() ${nameStringToVariableString(json.codeContext?.name)}Status = "${json.status}";
@Input() ${nameStringToVariableString(json.codeContext?.name)}LoadingText = "${json.activeText}";
@Input() ${nameStringToVariableString(json.codeContext?.name)}SuccessText = "${json.successText}";
@Input() ${nameStringToVariableString(json.codeContext?.name)}ErrorText = "${json.errorText}";
@Input() ${nameStringToVariableString(json.codeContext?.name)}SuccessDelay = ${json.successDelay === undefined ?
1500 : json.successDelay };`,
outputs: ({ json }) => `@Output() ${nameStringToVariableString(json.codeContext?.name)}OnSuccess = new EventEmitter();`,
imports: ['InlineLoadingModule'],
code: ({ json }) => {
return `<ibm-inline-loading
(onSuccess)="${nameStringToVariableString(json.codeContext?.name)}OnSuccess.emit($event)"
[successDelay]="${nameStringToVariableString(json.codeContext?.name)}SuccessDelay"
[state]="${nameStringToVariableString(json.codeContext?.name)}Status"
[loadingText]="${nameStringToVariableString(json.codeContext?.name)}LoadingText"
[successText]="${nameStringToVariableString(json.codeContext?.name)}SuccessText"
[errorText]="${nameStringToVariableString(json.codeContext?.name)}ErrorText"
${angularClassNamesFromComponentObj(json)}>
</ibm-inline-loading>`;
}
},
react: {
imports: ['InlineLoading'],
code: ({ json }) => {
const status = `state["${nameStringToVariableString(json.codeContext?.name)}"] || "${json.status}"`;
return `<InlineLoading
onSuccess={() => {
if (typeof state.${nameStringToVariableString(json.codeContext?.name)}OnSuccess === "function") {
state.${nameStringToVariableString(json.codeContext?.name)}OnSuccess();
}
}}
successDelay={${json.successDelay}}
description={${nameStringToVariableString(json.codeContext?.name)}StatusDescription}
iconDescription={${nameStringToVariableString(json.codeContext?.name)}StatusIconDescription}
status={${status}}
${reactClassNamesFromComponentObj(json)} />`;
},
additionalCode: (json) => {
const name = nameStringToVariableString(json.codeContext?.name);
const status = `${name}Status`;
const statusDescription = `${name}StatusDescription`;
const statusIconDescription = `${name}StatusIconDescription`;

return {
[status]: `const ${status} = {
active: {
iconDescription: "${json.activeIconDescription || 'Loading...'}",
description: "${json.activeText || 'Loading...'}"
},
error: {
iconDescription: "${json.errorIconDescription || 'Error!'}",
description: "${json.errorText || 'Error!'}"
},
inactive: {
iconDescription: "${json.inactiveIconDescription || ''}",
description: "${json.inactiveText || ''}"
},
finished: {
iconDescription: "${json.finishedIconDescription || 'Finished.'}",
description: "${json.successText || 'Finished.'}"
}
}`,
[statusDescription]: `const ${statusDescription} = ${status}[state["${name}"] || "${json.status}"].description`,
[statusIconDescription]: `const ${statusIconDescription} = ${status}[state["${name}"] || "${json.status}"].iconDescription`
};
}
}
}
};
3 changes: 3 additions & 0 deletions src/fragment-components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import * as textarea from './a-text-area';
import * as textinput from './a-text-input';
import * as link from './a-link';
import * as loading from './a-loading';
import * as inlineLoading from './a-inline-loading';
import * as overflowMenu from './a-overflow-menu';
// Tiles
import * as tile from './tiles/a-tile';
Expand Down Expand Up @@ -50,6 +51,7 @@ export { AFragment, AFragmentSettingsUI, AFragmentCodeUI } from './a-fragment';
export { AGrid, AGridSettingsUI } from './a-grid';
export { ALoading, ALoadingCodeUI, ALoadingSettingsUI } from './a-loading';
export { ALink, ALinkSettingsUI, ALinkCodeUI } from './a-link';
export { AInlineLoading, AInlineLoadingCodeUI, AInlineLoadingSettingsUI } from './a-inline-loading';
export { ANumberInput, ANumberInputSettingsUI, ANumberInputCodeUI } from './a-numberinput';
export { AProgressIndicator, AProgressIndicatorSettingsUI, AProgressIndicatorCodeUI } from './a-progress-indicator';
export { ARow, ARowSettingsUI } from './a-row';
Expand Down Expand Up @@ -87,6 +89,7 @@ export const allComponents = {
fragment,
grid,
loading,
inlineLoading,
radio,
radioGroup,
link,
Expand Down
56 changes: 56 additions & 0 deletions src/ui-fragment/src/components/ui-inline-loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import { InlineLoading } from 'carbon-components-react';
import { CssClasses } from '../types';

export interface InlineLoadingState {
type: string;
status: string;
activeIconDescription: string;
activeText: string;
errorIconDescription: string;
errorText: string;
inactiveIconDescription: string;
inactiveText: string;
finishedIconDescription: string;
successText: string;
successDelay: number;
cssClasses?: CssClasses[];
codeContext?: {
name: string;
};
}

export const UIInlineLoading = ({ state }: {
state: InlineLoadingState;
setState: (state: any) => void;
setGlobalState: (state: any) => void;
}) => {
if (state.type !== 'inline-loading') {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <></>;
}
const status: any = {
active: {
iconDescription: state.activeIconDescription,
description: state.activeText
},
error: {
iconDescription: state.errorIconDescription,
description: state.errorText
},
inactive: {
iconDescription: state.inactiveIconDescription,
description: state.inactiveText
},
finished: {
iconDescription: state.finishedIconDescription,
description: state.successText
}
};
return <InlineLoading
successDelay={state.successDelay}
description={status[state.status].description}
iconDescription={status[state.status].iconDescription}
status={state.status}
className={state.cssClasses?.map((cc: any) => cc.id).join(' ')} />;
};
4 changes: 4 additions & 0 deletions src/ui-fragment/src/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { UIDropdown } from './components/ui-dropdown';
import { UIExpandableTile } from './components/ui-expandable-tile';
import { UIGrid } from './components/ui-grid';
import { UILink } from './components/ui-link';
import { UIInlineLoading } from './components/ui-inline-loading';
import { UILoading } from './components/ui-loading';
import { UINumberInput } from './components/ui-number-input';
import { UIOverflowMenu } from './components/ui-overflow-menu';
Expand Down Expand Up @@ -151,6 +152,9 @@ export const renderComponents = (state: any, setState: (state: any) => void, set
case 'loading':
return <UILoading key={state.id} state={state} setState={setState} setGlobalState={setGlobalState} />;

case 'inline-loading':
return <UIInlineLoading key={state.id} state={state} setState={setState} setGlobalState={setGlobalState} />;

case 'radio-group':
return <UIRadioGroup key={state.id} state={state} setState={setState} setGlobalState={setGlobalState} />;

Expand Down

0 comments on commit 766732d

Please sign in to comment.