Skip to content

Commit

Permalink
chore (demo app) : add hyperview filter example (#928)
Browse files Browse the repository at this point in the history
asana: https://app.asana.com/0/1204008699308084/1208057868583175/f

add hyperview filter example to advanced tab -> custom components
dependent on: #927

---------

Co-authored-by: Florent Bonomo <[email protected]>
Co-authored-by: flochtililoch <[email protected]>
  • Loading branch information
3 people authored Aug 20, 2024
1 parent c0073ec commit 4a64cce
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 1 deletion.
142 changes: 142 additions & 0 deletions demo/src/Components/HyperviewFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import * as Logging from 'hyperview/src/services/logging';
import type { HvComponentProps, LocalName } from 'hyperview';
import Hyperview, {
Events,
LOCAL_NAME,
NODE_TYPE,
Namespaces,
} from 'hyperview';
import { useEffect } from 'react';

type FormDataPart = {
fieldName: string;
string: string;
};

declare class FormData {
getParts(): Array<FormDataPart>;
}
const FILTER_NS = 'https://instawork.com/hyperview-filter';

export const findElements = (node: Element, attributeNames: string[]) => {
if (node.nodeType !== NODE_TYPE.ELEMENT_NODE) {
return [];
}

if (
attributeNames.reduce(
(found, name) => found || !!node.getAttributeNS(FILTER_NS, name),
false,
)
) {
return [node];
}

return (Array.from(node.childNodes) as Element[])
.filter((child: Node | null) => {
return child !== null && child.nodeType === NODE_TYPE.ELEMENT_NODE;
})
.reduce((elements: Element[], child: Element) => {
elements.push(...findElements(child, attributeNames));
return elements;
}, []);
};

const HyperviewFilter = (props: HvComponentProps) => {
const onEventDispatch = (eventName: string) => {
const filterEvent =
props.element.getAttributeNS(FILTER_NS, 'on-event') || '';
if (filterEvent !== eventName) {
return;
}
const filterParam = props.element.getAttributeNS(FILTER_NS, 'on-param');
if (!filterParam) {
return;
}

const transformFlags: string[] = (
props.element.getAttributeNS(FILTER_NS, 'transform') || ''
).split(',');
const forceLowerCase: boolean = transformFlags.includes('lowercase');

const formData: FormData | null = props.options.componentRegistry
? ((props.options.componentRegistry.getFormData(
props.element,
) as unknown) as FormData)
: null;

const formPart: FormDataPart | undefined = formData
?.getParts()
.find(part => part.fieldName === filterParam);
const filterTerm = formPart ? formPart.string : '';
const transformedFilterTerm = forceLowerCase
? filterTerm.toLowerCase()
: filterTerm;

// Hide/show each element with filter terms or matching given regex. Modify attributes in-place
const filterElements: Element[] = findElements(props.element, [
'terms',
'regex',
]);
filterElements.forEach((element: Element) => {
const terms: string = element.getAttributeNS(FILTER_NS, 'terms') || '';
const regex: string | null =
element.getAttributeNS(FILTER_NS, 'regex') || null;
const termsArray: string[] = terms
.split(',')
.map((term: string) => (forceLowerCase ? term.toLowerCase() : term));
const showElement = (): boolean => {
if (regex) {
return new RegExp(regex).test(filterTerm);
}
return termsArray.some((term: string) =>
term.startsWith(transformedFilterTerm),
);
};
element.setAttribute('hide', String(!showElement()));
});

// Clone/swap the entire element to swap in the visibility changes
const newElement = props.element.cloneNode(true) as Element;
props.onUpdate(null, 'swap', props.element, { newElement });

// Set elements that need to render the filter term
findElements(newElement, ['role']).forEach((element: Element) => {
if (element.getAttributeNS(FILTER_NS, 'role') === 'filter-terms') {
if (
element.namespaceURI === Namespaces.HYPERVIEW &&
element.localName !== LOCAL_NAME.TEXT
) {
Logging.error(
'Element with attribute `role="filter-terms"` should be a <text> element or a custom element',
);
return;
}
const newRoleElement = element.cloneNode(true) as Element;
newRoleElement.textContent = filterTerm;
props.onUpdate(null, 'swap', element, { newElement: newRoleElement });
}
});
};

useEffect(() => {
Events.subscribe(onEventDispatch);
return () => {
Events.unsubscribe(onEventDispatch);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.element]);

return Hyperview.renderChildren(
props.element,
props.stylesheets,
props.onUpdate,
props.options,
);
};

HyperviewFilter.namespaceURI = FILTER_NS;
HyperviewFilter.localName = 'container' as LocalName;
HyperviewFilter.localNameAliases = [] as LocalName[];

export { HyperviewFilter };
3 changes: 2 additions & 1 deletion demo/src/Components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { HyperviewFilter } from './HyperviewFilter';
import { HyperviewSvg } from './HyperviewSvg';

export default [HyperviewSvg];
export default [HyperviewSvg, HyperviewFilter];
94 changes: 94 additions & 0 deletions examples/advanced/custom-components/filter/index.xml.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
permalink: "/examples/advanced/custom-components/filter.xml"
tags: custom_components
hv_button_behavior: "back"
hv_title: "Filter"
---
{% extends 'templates/base.xml.njk' %}

{% block custom_ns %}
xmlns:filter="https://instawork.com/hyperview-filter"
{% endblock %}

{% block styles %}
<style id="item" borderWidth="1" borderColor="#BDC4C4" marginHorizontal="24" padding="16" />
<style id="item-label" fontSize="16" />
<style id="input" fontSize="16" borderWidth="1" borderColor="#BDC4C4" padding="16" />
{% endblock %}
{% block container %}
<form>
<filter:container filter:on-event="search" filter:on-param="search-term" filter:transform="lowercase">
<list key="list">
<item key="field" style="item">
<text-field name="search-term" style="input" auto-focus="true" placeholder="Search">
<behavior trigger="change" action="dispatch-event" event-name="search" />
</text-field>
</item>
<item key="1" style="item" filter:terms="Reagan Bennett,Reagan,Bennett,San Francisco,San,Francisco">
<text style="item-label">Reagan Bennett</text>
<text>San Francisco</text>
</item>
<item key="2" style="item" filter:terms="Vicente Savage,Vicente,Savage,San,Jose,San Jose">
<text style="item-label">Vicente Savage</text>
<text>San Jose</text>
</item>
<item key="3" style="item" filter:terms="Alanna,Powers,Burlingame">
<text style="item-label">Alanna Powers</text>
<text>Burlingame</text>
</item>
<item key="4" style="item" filter:terms="Pablo,Hester">
<text style="item-label">Pablo Hester</text>
</item>
<item key="5" style="item" filter:terms="Sierra,Murphy">
<text style="item-label">Sierra Murphy</text>
</item>
<item key="6" style="item" filter:terms="Clark,Dawson">
<text style="item-label">Clark Dawson</text>
</item>
<item key="7" style="item" filter:terms="Marilyn,Reyes">
<text style="item-label">Marilyn Reyes</text>
</item>
<item key="8" style="item" filter:terms="Jerry,Wright">
<text style="item-label">Jerry Wright</text>
</item>
<item key="9" style="item" filter:terms="Madison,Peralta">
<text style="item-label">Madison Peralta</text>
</item>
<item key="10" style="item" filter:terms="Santos,Nelson">
<text style="item-label">Santos Nelson</text>
</item>
<item key="11" style="item" filter:terms="Nola,Woods">
<text style="item-label">Nola Woods</text>
</item>
<item key="12" style="item" filter:terms="Jeremy,Morton">
<text style="item-label">Jeremy Morton</text>
</item>
<item key="13" style="item" filter:terms="Ryann,Fowler">
<text style="item-label">Ryann Fowler</text>
</item>
<item key="14" style="item" filter:terms="Alfred,Santiago">
<text style="item-label">Alfred Santiago</text>
</item>
<item key="15" style="item" filter:terms="Rebekah,Acosta">
<text style="item-label">Rebekah Acosta</text>
</item>
<item key="16" style="item" filter:terms="Tobias,Russell">
<text style="item-label">Tobias Russell</text>
</item>
<item key="17" style="item" filter:terms="Harmony,Rocha">
<text style="item-label">Harmony Rocha</text>
</item>
<item key="18" style="item" filter:terms="Shawn,Bond">
<text style="item-label">Shawn Bond</text>
</item>
<item key="19" style="item" filter:terms="Malaysia,Campos">
<text style="item-label">Malaysia Campos</text>
</item>
<item key="20" style="item" filter:terms="Barrett,Li">
<text style="item-label">Barrett Li</text>
</item>
</list>
</filter:container>
</form>
{% endblock %}

0 comments on commit 4a64cce

Please sign in to comment.