Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/change billing form workflow #93

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
"react-router-dom": "^6.14.1",
"rxjs": "^6.6.7",
"swc-loader": "^0.2.3",
"swr": "^2.2.4",
"turbo": "^1.12.4",
"typescript": "^4.9.5",
"webpack": "^5.88.1",
Expand Down
4 changes: 2 additions & 2 deletions packages/esm-billing-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ehospital/esm-billing-app",
"version": "1.0.8",
"version": "1.0.9",
"description": "Billing frontend module for use in O3",
"browser": "dist/ehospital-esm-billing-app.js",
"main": "src/index.ts",
Expand Down Expand Up @@ -120,5 +120,5 @@
"*.{js,jsx,ts,tsx}": "eslint --cache --fix"
},
"packageManager": "[email protected]",
"gitHead": "9c01619bc902f645c76f4561b4c82f7fdb9d4a5e"
"gitHead": "7ccfa2e32cc7a985ad7e809fe6cbbf395a340311"
}
187 changes: 187 additions & 0 deletions packages/esm-billing-app/src/autosuggest/autosuggest.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import React, { type HTMLAttributes, useEffect, useRef, useState } from 'react';
import { Layer, Search, type SearchProps } from '@carbon/react';
import classNames from 'classnames';
import styles from './autosuggest.scss';

// FIXME Temporarily included types from Carbon
type InputPropsBase = Omit<HTMLAttributes<HTMLInputElement>, 'onChange'>;

interface SearchProps extends InputPropsBase {
/**
* Specify an optional value for the `autocomplete` property on the underlying
* `<input>`, defaults to "off"
*/
autoComplete?: string;

/**
* Specify an optional className to be applied to the container node
*/
className?: string;

/**
* Specify a label to be read by screen readers on the "close" button
*/
closeButtonLabelText?: string;

/**
* Optionally provide the default value of the `<input>`
*/
defaultValue?: string | number;

/**
* Specify whether the `<input>` should be disabled
*/
disabled?: boolean;

/**
* Specify whether or not ExpandableSearch should render expanded or not
*/
isExpanded?: boolean;

/**
* Specify a custom `id` for the input
*/
id?: string;

/**
* Provide the label text for the Search icon
*/
labelText: React.ReactNode;

/**
* Optional callback called when the search value changes.
*/
onChange?(e: { target: HTMLInputElement; type: 'change' }): void;

/**
* Optional callback called when the search value is cleared.
*/
onClear?(): void;

/**
* Optional callback called when the magnifier icon is clicked in ExpandableSearch.
*/
onExpand?(e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent<HTMLDivElement>): void;

/**
* Provide an optional placeholder text for the Search.
* Note: if the label and placeholder differ,
* VoiceOver on Mac will read both
*/
placeholder?: string;

/**
* Rendered icon for the Search.
* Can be a React component class
*/
renderIcon?: React.ComponentType | React.FunctionComponent;

/**
* Specify the role for the underlying `<input>`, defaults to `searchbox`
*/
role?: string;

/**
* Specify the size of the Search
*/
size?: 'sm' | 'md' | 'lg';

/**
* Optional prop to specify the type of the `<input>`
*/
type?: string;

/**
* Specify the value of the `<input>`
*/
value?: string | number;
}

interface AutosuggestProps extends SearchProps {
getDisplayValue: Function;
getFieldValue: Function;
getSearchResults: (query: string) => Promise<any>;
onSuggestionSelected: (field: string, value: string) => void;
invalid?: boolean | undefined;
invalidText?: string | undefined;
}

export const Autosuggest: React.FC<AutosuggestProps> = ({
getDisplayValue,
getFieldValue,
getSearchResults,
onSuggestionSelected,
invalid,
invalidText,
...searchProps
}) => {
const [suggestions, setSuggestions] = useState([]);
const searchBox = useRef(null);
const wrapper = useRef(null);
const { id: name, labelText } = searchProps;

useEffect(() => {
document.addEventListener('mousedown', handleClickOutsideComponent);

return () => {
document.removeEventListener('mousedown', handleClickOutsideComponent);
};
}, [wrapper]);

const handleClickOutsideComponent = (e) => {
if (wrapper.current && !wrapper.current.contains(e.target)) {
setSuggestions([]);
}
};

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const query = e.target.value;
onSuggestionSelected(name, undefined);

if (query) {
getSearchResults(query).then((suggestions) => {
setSuggestions(suggestions);
});
} else {
setSuggestions([]);
}
};

const handleClear = (e: React.ChangeEvent<HTMLInputElement>) => {
onSuggestionSelected(name, undefined);
};

const handleClick = (index: number) => {
const display = getDisplayValue(suggestions[index]);
const value = getFieldValue(suggestions[index]);
searchBox.current.value = display;
onSuggestionSelected(name, value);
setSuggestions([]);
};

return (
<div className={styles.autocomplete} ref={wrapper}>
<label className="cds--label">{labelText}</label>
<Layer className={classNames({ [styles.invalid]: invalid })}>
<Search
id="autosuggest"
onChange={handleChange}
onClear={handleClear}
ref={searchBox}
className={styles.autocompleteSearch}
{...searchProps}
/>
</Layer>
{suggestions.length > 0 && (
<ul className={styles.suggestions}>
{suggestions.map((suggestion, index) => (
<li key={index} onClick={(e) => handleClick(index)} role="presentation">
{getDisplayValue(suggestion)}
</li>
))}
</ul>
)}
{invalid ? <label className={classNames(styles.invalidMsg)}>{invalidText}</label> : <></>}
</div>
);
};
62 changes: 62 additions & 0 deletions packages/esm-billing-app/src/autosuggest/autosuggest.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@use '@carbon/styles/scss/spacing';
@use '@carbon/styles/scss/type';
@use '@openmrs/esm-styleguide/src/vars' as *;

.label01 {
@include type.type-style('label-01');
}

.suggestions {
position: relative;
border-top-width: 0;
list-style: none;
margin-top: 0;
max-height: 143px;
overflow-y: auto;
padding-left: 0;
width: 100%;
position: absolute;
left: 0;
background-color: #fff;
margin-bottom: 20px;
z-index: 99;
}

.suggestions li {
padding: spacing.$spacing-05;
line-height: 1.29;
color: #525252;
border-bottom: 1px solid #8d8d8d;
}

.suggestions li:hover {
background-color: #e5e5e5;
color: #161616;
cursor: pointer;
}

.suggestions li:not(:last-of-type) {
border-bottom: 1px solid #999;
}

.autocomplete {
position: relative;
}

.autocompleteSearch {
width: 100%;
}

.suggestions a {
color: inherit;
text-decoration: none;
}

.invalid input {
outline: 2px solid var(--cds-support-error, #da1e28);
outline-offset: -2px;
}

.invalidMsg {
color: var(--cds-text-error, #da1e28);
}
Loading
Loading