Giter Site home page Giter Site logo

ing-bank / lion Goto Github PK

View Code? Open in Web Editor NEW
1.7K 31.0 281.0 62.92 MB

Fundamental white label web component features for your design system.

Home Page: https://lion-web.netlify.app/

License: MIT License

JavaScript 96.98% HTML 0.47% Shell 0.01% TypeScript 2.54%
hacktoberfest lion lit-html javascript web-components modern-web open-wc

lion's Introduction

Lion

Lion

Lion open issues status GitHub open pull requests status Todos

Website · Fundamentals · Guides · Components · Blog

Lion is a set of highly performant, accessible and flexible Web Components.!

They provide an unopinionated, white-label layer that can be extended to your own layer of components.

  • High Performance: Focused on great performance in all relevant browsers with a minimal number of dependencies.
  • Accessibility: Aimed at compliance with the WCAG 2.2 AA standard to create components that are accessible for everybody.
  • Flexibility: Provides solutions through Web Components and JavaScript classes which can be used, adopted and extended to fit all needs.
  • Modern Code: Lion is distributes as pure es modules.
  • Exposes functions/classes and Web Components: Ships a functionality in it's most appropriate form.

Note: Our demos may look a little bland but that is on purpose. They only come with functional stylings. This makes sense as the main use case is to extend those components and if you do you do not want to override existing stylings.

Explore the Lion Guides  ▶

How to install

npm i @lion/ui

How to use

Extend a Web Component

This is the main use case for lion. To import component classes, and extend them for your own design system's components.

import { css } from 'lit';
import { LionInput } from '@lion/ui/input.js';

class MyInput extends LionInput {
  static get styles() {
    return [
      super.styles,
      css`
        /* your styles here */
      `,
    ];
  }
}
customElements.define('my-input', MyInput);

Use a JavaScript system

There's a couple of "systems" in lion which have a JavaScript API. Examples are localize, overlays, ajax, etc.

<script type="module">
  import { ajax } from '@lion/ui/ajax.js';

  ajax
    .fetch('data.json')
    .then(response => response.json())
    .then(data => {
      // do something with the data
    });
</script>

Use a Web Component

You can also use the lion elements directly, although this is likely not a common use case.

<script type="module">
  import '@lion/ui/define/lion-input.js';
</script>

<lion-input name="firstName"></lion-input>

Issues

If you encounter an issue with any of the packages we are offering please open a new bug issue. Be sure to include a description of the expected and the current behavior - additional adding a reproduction always helps.

Feature requests

When you have an idea on how we could improve, please check our discussions to see if there are similar ideas or feature requests. If there are none, please start your feature request as a new discussion topic. Add the title [Feature Request] My awesome feature and a description of what you expect from the improvement and what the use case is.

Content

Name version description
@lion/ui @lion/ui version Set of components
@lion/ajax @lion/ajax version A small wrapper around fetch
Singleton-manager Singleton-manager version A singleton manager provides a way to make sure a singleton instance loaded from multiple file locations stays a singleton. Primarily useful if two major version of a package with a singleton is used.
Babel plugin extend docs babel-plugin-extend-docs version A plugin which rewrites imports and templates according to a configuration. This enables the reuse of existing documentation from source packages while still using your extensions code.
Providence analytics providence-analytics version Providence is the 'All Seeing Eye' that generates usage statistics by analyzing code. It measures the effectivity and popularity of your software. With just a few commands you can measure the impact for (breaking) changes, making your release process more stable and predictable.
Publish docs publish-docs version A tool that copies and processes your documentation (in a monorepo) so it can be published/shipped with your package.
Remark extend remark-extend version A plugin for remark to extend markdown by importing from source files.
Rocket preset extend lion docs rocket-preset-extend-lion-docs version When maintaining your own extension layer of lion you most likely want to maintain a similar documentation. Copying and rewriting the markdown files works, but whenever something changes you need copy and rewrite again. To do this automatically you can use this preset for rocket.

Technologies

Lion Web Components aims to be future-proof and use well-supported proven technology. The stack we have chosen should reflect this.

Rationale

We know from experience that making high quality, accessible UI components is hard and time consuming: it takes many iterations, a lot of development time and a lot of testing to get a generic component that works in every context, supports many edge cases and is accessible in all relevant screen readers.

Lion aims to do the heavy lifting for you. This means you only have to apply your own Design System: by delivering styles, configuring components and adding a minimal set of custom logic on top.

Coding guidelines

Check out our coding guidelines for more detailed information.

How to contribute

Please note: This project uses Npm Workspaces. If you want to run all demos locally you need to get at least npm 7+ and install all dependencies by executing npm install.

Lion Web Components are only as good as its contributions. Read our contribution guide and feel free to enhance/improve Lion. We keep feature requests closed while we're not working on them.

Contact

Feel free to create a github issue for any feedback or questions you might have.

lion's People

Contributors

aymenbenamor avatar cublion avatar dakmor avatar dannymoerkerke avatar dependabot[bot] avatar erikkroes avatar gerjanvangeest avatar github-actions[bot] avatar gserb avatar gyulus3 avatar hardikpthv avatar hzunax avatar jaysunsyn avatar jcperea avatar jorenbroekema avatar knorgias avatar koddsson avatar larsdenbakker avatar marcosgilf avatar mathieupuech avatar narzac avatar okadurin avatar ovidiu1 avatar palash2601 avatar riovir avatar t-motion avatar thepassle avatar tlouisse avatar whenlit avatar wsloth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lion's Issues

[icon] icon loading system

I proposed this system already internally, moving it to github now.

The current icon system is a potential footgun for performance. I'd like to propose a change which improves performance and maintains a good developer experience.

In the current system the easiest way to use icons is by importing them synchronously. This method is also the default promoted/documented method. Sync imports will include the icons with the initial bundle of the application which for large applications can really add up to quite an amount.

Applications often have areas which are dynamic where it is not known beforehand what kind of content and icons will be shown. For example a user might be able to choose a profile picture from a set of icons, or the application might display a list of items where each item might have an icon based on some data from an API. In these case the list of possible icons is basically 'everything', resulting in all icons having to be imported and increasing the bundle size.

It is possible to use dynamic imports to load the icons, but this requires quite some extra boilerplate and specific knowledge. Also different teams might work on different areas of the application, and end up rewriting the same dynamic loading logic for the same icons.

Because lazy loading is not the default promoted and documented method most people will end up with the synchronous icon system anyway and (hopefully) only later find out that they need to refactor this to improve performance.

I propose a system similar to the localization system, and similar to the way iron-icon works. Developers use the icon element and specify a specific icon-id:

<lion-icon icon-id="lion:space:meteor"></lion-icon>

The icon id is made up of a specific set of parts: <namespace>:<collection>:<icon>. For each namespace a loader is defined which then lazily loads the icons on demand. For example:

icons.addIconResolver('lion', async (collection, iconName) => {
  switch (collection) {
    case 'bugs':
      return (await import('./icons/bugs-collection.js'))[iconName];
    case 'space':
      return (await import('./icons/space-collection.js'))[iconName];
    default:
      throw new Error(`Unknown collection: ${collection}`);
  }
});

In the example above a space icon is requested, so the space collection is imported and the meteor icon is returned. When another space icon is requested, the space collection was already imported so it won't be loaded again. It's better to bundle a set of icons together and import it, instead of doing separate imports for each icon.

Benefits:

  • Icons are always lazy loaded and never end up in the main bundle.
  • Icons are always loaded on demand
    • For example if you display icons in a menu, the menu isn't rendered until it is opened so the icons are not imported until the menu is opened as well.
  • There isn't any special syntax to learn, so people get performance for free.
  • Icon ids can be defined from any arbitrary source, so you can pass icon names from an API or a CMS system directly to the icon component.
  • Icons behave just like images.
  • Icons can potentially be loaded only when in view using IntersectionObserver

I implemented this already in my own application, with an extension on lion-icon, and it works very well for us.

[input-date] Empty date value on form data is `Unparseable`

Expected Behavior

On previous versions, I was able to use the <lion-input-date> without a default value (.modelValue), and leave it blank when submitting the form since it's optional.

Current Behavior

Upon loading of the page that contains the form, if I try to submit the form (with empty date), the date input from the form data is identified as Unparseable. If I try to input a valid date, then remove the value, upon submitting the form, it is then able to allow an empty date input.

Steps to Reproduce

  1. Add an lion-input-date element to a form.

  2. Leave the input date empty

  3. Submit the form

If you check the contents of the form data from the submit method, it contains a date property with Unparseable as its value.

Run tests in all supported browsers on BrowserStack

Note: if i'm running using Chrome ( bs_win10_chrome_latest) all the tests are running.

I try to run all the tests on IE11 (bs_win10_ie_11), and i got something like this:

λ npm run test:bs

> @lion/root@ test:bs D:\dev\lab\lion
> karma start karma.bs.config.js --coverage


START:
18 06 2019 21:28:51.979:WARN [filelist]: Pattern "D:/dev/lab/lion/__snapshots__/**/*.md" does not match any file.
18 06 2019 21:28:53.547:INFO [karma-server]: Karma v4.1.0 server started at http://0.0.0.0:9876/
18 06 2019 21:28:53.548:INFO [launcher]: Launching browsers bs_win10_ie_11 with concurrency unlimited
18 06 2019 21:28:53.607:INFO [launcher]: Starting browser IE 11.0 (Windows 10) on BrowserStack
18 06 2019 21:29:00.208:INFO [launcher.browserstack]: IE 11.0 (Windows 10) session at https://automate.browserstack.com/builds/a48343450ca0f66a4f1ac1da96d4536c314fe859/sessions/69d851431e3a82dc9cc70a483c21ff536a188c53
18 06 2019 21:29:05.135:INFO [IE 11.0.0 (Windows 10.0.0)]: Connected on socket RDJX-pPfYMpMcPdXAAAA with id 79747197

Finished in 0.003 secs / 0 secs @ 21:29:07 GMT+0300 (Eastern European Summer Time)

SUMMARY:
√ 0 tests completed

=============================== Coverage summary ===============================
Statements   : Unknown% ( 0/0 )
Branches     : Unknown% ( 0/0 )
Functions    : Unknown% ( 0/0 )
Lines        : Unknown% ( 0/0 )
================================================================================
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @lion/root@ test:bs: `karma start karma.bs.config.js --coverage`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @lion/root@ test:bs script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

I think is a problem with browserstack, also i found this issue.

Pilot Phase

Lion Web Components are still in an early alpha stage; they should not be considered production ready yet.

The goal of our pilot phase is to gather feedback from a private group of users. Therefore, during this phase, we kindly ask you to:

  • not publicly promote or link us yet: (no tweets, blog posts or other forms of communication about Lion Web Components)
  • not publicly promote or link products derived from/based on Lion Web Components

Feel free to subscribe to this issue to be notified as soon as the Pilot Phase ends.

Elements don't support self closing tags

Heyo,

Not sure this is something that can/should be fixed, but I just banged my head against it so I thought it was worth mentioning.

I'm converting an existing form using generic HTML tags into lion.

I was using lots of self-closing tags :

<input 
        id="accessToken" 
        name="accessToken" 
        placeholder="accessToken">

converted it to :

<lion-input 
        label="Personal Access Token" 
        id="accessToken" 
        type="password" 
        name="accessToken" 
        placeholder="accessToken">

Nothing to be seen on my page.

I looked into imports and all, searching for a generic web-components related imports error, but the issue was simply that I was missing the closing tag </lion-input>.
Consequence was that I got nested elements, breaking the page :).

(calendar) test fails on master branch

One of the test for lion-calendar is failing because it depends on actual day but is compared to constant day.

Test name: is on day closest to today, if today (and surrounding dates) is/are disabled

AssertionError: expected 21 to equal 17

[input-datepicker] Wrong styles of the invoker button when `readonly` or `disabled`

image

I found out why there is a difference between how disabled and readonly buttons looks like

for disabled we have this style:

            :host(.state-disabled) .input-group ::slotted(*) {
              color: var(--disabled-text-color, #adadad);
            }

not for readonly though

this is just a side-effect of course, there was no intention to make a different colour of the button, but it is a bug, because in both cases the button should look like disabled and in both cases we need consistent styles which we just don't define and face a strange inconsistent side effect

_Originally posted by @bashmish in #105 (comment)

[overlays] Tooltip / Popup don't respond to programmatically changing the placement

Note: This issue is based on the Popper POC , but is also an issue for the old implementation

I would expect the tooltip/popup to change position when I change the placement property programmatically.

There is nothing that handles updates of this property currently, and I think we should implement that it changes the popper config and calls popper.update() (if im not mistaken) to update the position.

This will not only help our users, but allow us to make an attractive demo to show all positions and let the user use Storybook Knobs to change it to how they like :).

[form] [proposal] consider removing FieldCustomMixin

Originally, the FieldCustomMixin was created to be used in conjunction with 'specialized' fields (input-iban, input-amount, input-date etc.) having defaults for help-texts, labels etc.

In the Lion versions of the specialized inputs mentioned above we have no default help-texts/labels applied. Therefore, the FieldCustomMixin has more right of existence on more opinionated, extending layers (not in Lion repo).

Currently, its functionality contains only the disabling of help-texts. For maximum flexibility, on extending layers it should contain:

  • disable predefined help-texts and labels
  • make sure that a label/help-text defined by an app developer 'wins' from a predefined one (this involves some logic since localisation resources are often lazily loaded)

If we would decide however to keep this mixin, we should rename it to something like 'SpecializedFieldMixin', to avoid confusion with custom field wrappers like described here: https://github.com/ing-bank/lion/tree/master/packages/field

[button] tabindex is not set correctly for disabled button in IE11

This test currently fails in IE11

it('disabled does not override user provided tabindex', async () => {
  const el = await fixture(`<lion-button tabindex="5" disabled>foo</lion-button>`);
  expect(el.getAttribute('tabindex')).to.equal('-1');
  el.disabled = false;
  await el.updateComplete;
  expect(el.getAttribute('tabindex')).to.equal('5');
});

[field] form elements are not being registered in Edge

For some weird reason, in Edge (IE is fine) parent form elements don't seem to receive the form-element-register event and thus doesn't register its child elements. When using derived fieldsets as radio-group some of the methods and properties don't work as expected.

[select] modelValue is not being properly set once the initial modelValue property is empty

Steps to replicate:

  1. Use lion-select
  2. Default modelValue is empty (using property) <lion-select .modelValue="${this.someProperty}">
  3. Once the property in the modelValue changes, the display in the lion-select element doesn't display the value but when trying to use $0.modelValue, there is a value.

Vice versa, when I use value instead of modelValue like <lion-select .value="${this.someProperty}">, the display is updated with the correct value BUT the modelValue is empty preventing the form to be submitted.

As a workaround, I used <lion-select .value="${this.someProperty}" .modelValue="${this.someProperty}">

[icon] Renders 'undefined'

When you create a new <lion-icon> element, the svg property is initially undefined, and it renders nothing (blank space).

When I explicitly set the svg property to the value undefined (after it being set as something else) it renders the text "undefined" instead of rendering blank space.

I expect the element to render nothing when I set the svg property to undefined.

[form][input] Reset not working after submitting invalid inputs

Steps to Replicate

  1. Go to form reset demo
  2. Enter invalid inputs for first name and last name
  3. Click Submit button
  4. Click Reset button

Expected Behaviour

Clicking reset button should empty the input elements

Current Behaviour

Invalid input is retained in value propert. However, when you inspect the modelValue, it is already cleared. Even if you manually delete the value, the element is putting it back.

add Typescript definitions

Since lit-element is the base of Lion webcomponents and since lit-element support (pretty nicely) typescript, it would be great to have definitions files shipped with lion too.

As a kickstart, here is the one I started creating:

declare module '@lion/ajax' {
    import { AxiosRequestConfig, AxiosPromise, AxiosError } from 'axios';

    namespace ajax {
        export type Promise<T> = AxiosPromise<T>;
        export type Configuration = AxiosRequestConfig;
        export type Error = AxiosError;
        export type Data = Record<string, unknown>;
        export function get<T>(url: string, configuration?: AxiosRequestConfig): Promise<T>;
        function del<T>(url: string, configuration?: AxiosRequestConfig): Promise<T>;
        export { del as delete };
        export function patch<T>(url: string, data: Data, configuration?: AxiosRequestConfig): Promise<T>;
        export function post<T>(url: string, data: Data, configuration?: AxiosRequestConfig): Promise<T>;
        export function put<T>(url: string, data: Data, configuration?: AxiosRequestConfig): Promise<T>;
    }
}

declare module '@lion/core' {
    export * from 'lit-element';
    export * from 'lit-html/directives/if-defined';
}

declare module '@lion/localize' {
    import { LitElement } from 'lit-element';

    namespace localize {
        export function loadNamespace(namespace: Record<string, (locale: string) => Promise<unknown>>): void;
        export function addEventListener(eventName: 'localeChanged', callback: () => void): void;
        export function msg(key: string, variables: Record<string, unknown>): string;
        export let locale: string;
    }

    class LocalizeClassMixin extends LitElement {
        public static waitForLocalizeNamespaces: boolean;

        public static localizeNamespaces: (Record<string, (locale: string) => Promise<unknown>>)[];

        public performUpdate(): Promise<unknown> | void;

        public connectedCallback(): void;

        public disconnectedCallback(): void

        public msgLit(key: string, variables?: Record<string, unknown>, options?: Record<string, unknown>): void | string;

        public onLocaleReady(): void;

        public onLocaleChanged(): void;

        public onLocaleUpdated(): void
    }

    export function LocalizeMixin(superClass: typeof LitElement): typeof LocalizeClassMixin;
}

[overlays] Open up tooltip and popup to placementConfig overrides

Note: based on Popper POC where the API changes to 1 placementConfig object.

Currently, the only override the user of Lion can do is the placement (or previously called position), but we should allow the user to override the entire placementConfig. LocaloverlayController already allows this :).

Right now, for a user to create their own branded popup, they would have to extend lion-popup and override the entire connectedCallback method (and therefore, create duplicate code). The more friendly way, that would be enabled by this change, would be to set their own default placementConfig in the constructor. This would in turn allow their end users to override it using the following syntax <branded-popup .placementConfig=${myPlacementConfig}>

Should make sure of course that programmatically changing this config should also re-render it to reflect those changes for which I also made an issue #103

[field] values switching from invalid to valid are not formatted (and vice versa)

Problem

Fields containing valid modelValues should be run through a formatter, whereas invalid modelValues should not.
Values switching from invalid to valid are not formatted (after this pull request is merged they are) and values switching from valid to invalid will be formatted (but should not).

The pr mentioned provides a temporary workaround, but a proper solution is needed.

Current flow

Setting of modelValue triggers observers for:

  • computation of 'formattedValue'
  • validation (and thus computation of 'errorState')

Observers will be handled in above order. Since computation of formattedValue depends on 'errorState' (erroneous inputs should not be formatted), the formatter is handled with a not yet updated errorState.

Solution

Rewrite to a flow handling observers in this order: modelValue -> errorState(validation) -> formattedValue.
Important: in the referenced pr, a situation where a value changes from valid to invalid will still lead to formatting when not required.
Since the flow above involves two mixins (FormatMixin and ValidateMixin) and ValidateMixin should have no knowledge about FormatMixin, think of a proper way to do this.

It might be as easy as changing the order of mixins applied within LionField: if the ValidateMixin runs before the FormatMixin, the errorState should be computed by the time the formatter runs.

[form][field] properly demo main Field/FormControl functionality

Currently there are no proper demos for:

  • interaction states
  • advanced validation
  • formatters/parsers
  • modelValues and best practices

Those are like the form core functionalities and should be demoed individually instead of being scattered across individual element demos.

[form] errorState is not recomputed properly

Description

Having an lion-form with one lion-input marked as required, when this lion-input stops being rendered the form shouldn't reflect an error state.

Expected Behavior

lion-form can unregister inputs that stops being rendered in the form, so then the errorState is correct when the form is submitted.

Current Behavior

lion-form doesn't unregister inputs that stops being rendered in the form, so then the errorState doesn't reflect the real situation.

Steps to Reproduce

I have created a web component and a test for reproducing the bug, here the steps in the case that you want to test it manually:

1- Click Submit, errorState should be true because the ing-input "name" is required.

2- Click in the button Toggle is name visible

3- The ing-input "name" is not rendered anymore

4- Click Submit, the errorState should be false since there isn't any required input within the form

lint:eclint script not working in Windows

The lint:eclint script currently don't work on windows machines.

The find command and the $ only works in unix environments, so probably it can be simplified and ignore unwanted files in the editorconfig file.

[form][field] `.formattedValue` should be set to undefined when not being able to format

Currently, this happens:

        if (this.modelValue instanceof Unparseable) {
          return this.modelValue.viewValue;
        }
        if (this.errorState) {
          return this.inputElement ? this.value : undefined;
        }
        return this.formatter(this.modelValue, this.formatOptions);
      }

https://github.com/ing-bank/lion/blob/master/packages/field/src/FormatMixin.js#L186

It should not return the current value of the inputElement and sync it back. Whenever the formattedValue returns undefined, it should be decided in the sync down step to leave inputElement.value as is

[localization] make validator messages externally overridable

Some of the default messages that are used by the validators do not work very well in all contexts. If you have a long label name for an input that is required you might get something such as 'Fill Do you already have an account? in.' Naturally, that is not desirable.

Currently we work around this issue by providing a fixed message on the global scope (i.e. 'This field is required.'), but we would like to be able to override the message on a per-field basis, without wrapping the input element.

Tried to have a look at implementing this myself, but I cannot see a proper way of doing this. Took a reasonably quick look at the validator mixin and the localization mixin. Any suggestions?

[localize] Make all components forward compatible with dialects

I want all components to work with all locales for languages we have in ./translations/, while at the same time support polymer-cli for the ones we manually define in static get localizeNamespaces().

It is easier to illustrate my idea with the code.

Imagine we have ./translations/en.js and such code:

static get localizeNamespaces() {
  return [
    {
      'lion-component': locale => {
        switch (locale) {
          case 'en-GB':
          case 'en':
            return import('../translations/en.js');
          default:
            throw new Error(`Unknown locale: ${locale}`);
        }
      },
    },
    ...super.localizeNamespaces,
  ];
}

Then this code will work for en-AU and en-US thanks to just one change and this feature of localize:

static get localizeNamespaces() {
  return [
    {
      'lion-component': locale => {
        switch (locale) {
          case 'en-GB':
          case 'en':
            return import('../translations/en.js');
          default:
            return import('../translations/${locale}.js'); // changed
        }
      },
    },
    ...super.localizeNamespaces,
  ];
}

This implies that fact that 2 requests will be needed for locales which don't have a file in ./translations/, because this is how this feature works. But we knew that when we designed it and that was a trade-off, it is just weird we are not using it since then ourselves.

This forward compatibility is useful for people using native dynamic imports or/and webpack for bundling, because they can then use existing languages that we already support, e.g. English, for other countries. For example, most of changes like this for "en-PH" will not be needed for them, unless they also need to support polymer-cli.

I would say the fact we are not forward compatible with modern standards is a bug.

Not able to see any demo output under canvas tab

I setup the lion project and ran using npm run start but not able to see output in chrome and safari. Here are the respective versions.

OS : MacOS 10.14.1
Chrome: Version 75.0.3770.100

Please see the screenshot below.

Screenshot 2019-06-18 at 7 00 27 PM

Button has non-technical styling

lion-button includes non-technical styling by default. This styling needs to then be overwritten by any design system implementing the button. This seems wasteful.

[form][field] improve documentation FormatMixin

It's often unclear for people how the 'value computation loop' works.

  • discriminate between different scenarios in which the loop can be triggered:
 * [1] Application Developer sets `.modelValue`:
 *     Flow: `.modelValue` -> `.formattedValue` -> `.inputElement.value`
 *                         -> `.serializedValue`
 * [2] End user interacts with field:
 *     Flow: `@user-input-changed` -> `.modelValue` -> `.formattedValue` - (debounce till reflect condition (formatOn) is met) -> `.inputElement.value`
 *                                 -> `.serializedValue`
  • annotate code explaining when different flows are used
  • make more clear how format conditionals hook into the above

[form] [proposal] exporting input-datepicker utils

When writing tests on forms, interacting with various inputs is viable. While working with the input-datepicker I found these test utils very helpful and time saving: https://github.com/ing-bank/lion/blob/master/packages/input-datepicker/test/test-utils.js

Exporting the test-utils
Making the utils available for public usage would make testing some steps easier and time saving. Especially in this case, where one must understand which components play together, e.g. date input, calendar overlay, selecting days and months etc.

[proposal] allow lit-html in SlotMixin

When the SlotMixin was designed, we aimed for a template system agnostic solution.
Since we embraced lit-html, let’s leverage its power also in SlotMixin.

It would change this:

label: () => {
  const label = document.createElement('label');
  label.textContent = this.label;
  return label;
},
'help-text': () => {
  const helpText = document.createElement('div');
  helpText.textContent = this.helpText;
  return helpText;
},

to this:

label: () => html`<label>${this.label}</label>`,
'help-text': () => html`<div>${this.helpText}</div>`,



It’s more concise and readable.
It would also be more in line with our existing templates (considering the fact that the slots are part of the template and are defined outside the main render() for accessibility reasons only.)

@daKmoR @bashmish since you were both involved in the creation of this mixin as well, do you think it would be a nice improvement?

New Stackblitz (Turbo-resolver) projects broken

Using @lion/core in new projects on Stackblitz is not working anymore. Here is a fork of a project posted 8 days ago in one of the other issues: https://stackblitz.com/edit/lion-form-9r5wtq The only thing I changed is reinstalling the dependencies. Now, the app doesn't seem to be running anymore.

When looking in the sources of the example we see files of 2 versions of lit-html being loaded (1.0.0 and 1.1.0):

image

After some investigation this is happening because of 3 separate things coming together:

  • Stackblitz's module resolver (Turbo) is fetching the dependencies for every module on the fly from unpkg.com. To know which version to download, Turbo will only look at the module's own package.json. (npm and yarn will look at all dependencies and subdependencies)
  • @lion/core is depending on a fixed version of lit-html: 1.0.0
  • lit-element 2.1.0 is depending on lit-html: "^1.0.0", which is resolving to 1.1.0 since Tuesday (21/5), as a new version has been released.

All of this affects the internal wiring of LitElement, preventing the TemplateResult to be rendered in the renderRoot.

As lion is a library I think the best solution here is to have @lion/core be dependent on ranges of lit-element and lit-html instead of fixed versions.

[button] Buttons are announced twice in screen readers

Testing out the storybook with the NVDA screenreader I immediately notice that the buttons are read out twice (except for the button consisting only of an icon).

Inspecting the DOM, I notice that the lion-button element is given role="button". However, they also contain <button> children. These are given tabindex="-1", and indeed if I navigate by tab each button is only read out once. However since the button elements are not hidden from the a11y tree I can still navigate to them with the screenreader virtual cursor (in my case by pushing the down arrow button). The first button for example is announced as "Button default" in NVDA. Moving the virtual cursor down then announces "Button clickable".

Demo: https://js-mu9ixf.stackblitz.io (edit: https://stackblitz.com/edit/js-mu9ixf)

Affected browsers and screen readers:

  • latest Chrome and Firefox with NVDA 2018.1.1 and 2019.1.1
  • IE11 with JAWS

[button] programmatic clicks don't work on mobile browsers when combined with polymer

The polymer gestures modules prevents programmatic clicks on mobile browsers. This is due to the fact that their event detection mechanism doesn't correctly detect the click events.

There is an open issue Polymer/polymer#5289 but I don't know if it will be solved anytime soon.

The lion project doesn't officially have anything to do with polymer, but we use lion-web and polymer in hybrid applications.

Possible solutions I can think of:

  • Call window.Polymer.Gestures.resetMouseCanceller(); before calling .click();
  • Ensure that the button we call .click() on is a child of ing-button, so that the detection works properly
  • Fire input.dispatchEvent(new MouseEvent('click', { bubbles: false })) instead of a click

Any other ideas?

[overlays] [proposal] improvements

While building the datepicker, I encountered a few missing features/shortcomings in the Overlay System:

- auto a11y for invokers
The overlay system takes care of all accessibility, the only thing that it doesn't take care of is the attrs for global overlays (aria-haspop="dialog" and aria-expanded="false/true" (should be toggled programatically when global overlay is non-modal)

- hidesOnOutsideClick support for global overlays.
A modal overlay shouldn't be closeable, but a global overlay is not neccessarily a modal overlay and should thus support this feature like localOverlay does (example: material design datepicker).

- No horizontal centering of dialog
Currently, the global dialog opens to the left of the screen. I think the default should be horizontally centered?

- No vertical centering of dialog
Currently, it opens ‘below the end of the page’. That means there is no relation to invoker element. Think the default should be either a fixed number from top of window or always vertically centered

- Not closing on esc when clicking on backdrop -> focus on body
A modal dialog allows to click on backdrop and then body becomes the document.activeElement.

- elementToFocusAfterHide -> doesn’t work
Didn't check thoroughly, but I think it's not working (not when provided as arg to show method, also not when provided as prop 'elementToFocusAfterHide' to config.)

- Smarter defaults: always need to add these to my own styling:
- Position: relative so that it is positioned above backdrop
- Background: white (Just like HTML body. You can override to transparent or other color if needed.)

- Shadow dom encapsulation by default
The only way to make content put in global overlay not leak styles, is to put it in the web component you created yourself.

Imagine this:

    const myCtrl = overlays.add(
      new ModalDialogController({
        contentTemplate: () => html`<div class="overlay"><div class="overlay-heading">bla</div></div>`,
      }),
    );

Now this overlay should be aware of its context. If it should be used in an app that could be placed in whatever legacy context, it can easily conflict when generic names are used. (a legacy app with .overlay { background: yellow } would cause inward leaking.
Something like this already happened to one of our consumers.

The current solution, always providing our own web component, requires a lot more boilerplate (it's a bit cumbersome if you want to provide an api for Subclassers in the input-datepicker component). Also, if you provide content projection inside your web component, you would be exposed to global scope again. I think providing one layer of encapsulation above this, would solve this problem.

Also, .global-overlays can be encapsulated by a shadow root to make sure it will never conflict with anything defined outside of it (this is actually the real conflict we encountered, when it was called 'overlay' and clashed with a 3rd party lib).

I think shadow dom encapsulation should be the default with an opt-out (if you want to hook in to a globally defined style lib, only for compatibility with legacy apps).

- Styling not configurable
I want to be able to change the colour, opacity, animation of the backdrop. Also want to override vertical placement etc. Making the global-overlays a wc, would make this easier and in line with other 'style patchings' for Subclassers.

@LarsDenBakker, @bashmish, @jorenbroekema, @daKmoR:
Could you please give your thoughts about the above?
Some of them might be design considerations and I might miss something in my proposed solutions?

Not able to run or build the project

Not able to run the project after git clone and npm install. npm run start failing for many dependencies. Tried building by running npm run storybook:build that too failing.

Please see the screenshot for npm run start

Screenshot 2019-05-22 at 10 46 19 PM

For npm run storybook:build

Screenshot 2019-05-22 at 10 53 39 PM

Node :v8.9.4
npm : 6.7.0

Commonjs, ES and UMD modules building for lion components

As per my knowledge about the libraries. It provides Commonjs, es and umd modules as output to consumed by projects. I don't see these modules in lion. The source files being scattered in root directory which npm install lion provides to consume. These modules are useful because it would be tested compiled and compressed. The Commonjs and es modules doesn't have any impact if the project uses any build script and does all building task. But umd modules gives ability to use the library through the browser and check any components without a build script. This is very useful when you want to show the example through plunker or stackblitz.

I would like to configure the build script and would also like to organise the code for components being scatted at root level.

  • build:commonjs: will build the CommonJS version in the dist/lib folder
  • build:es: will build the ES2015+ modules version in the dist/es folder
  • build:umd: will build the UMD version at dist/umd/lion.js (with sourcemaps)
  • build:umd:min: will build the minified UMD version at dist/umd/lion.min.js

[core][proposal] DelegateMixin improvement

DelegatesMixin

(improvement proposal for to be deprecated DelegateMixin)

Composition is a very common pattern for our UI components, especially since it is not possible to extend native elements.
Often we do want to (partly) keep the original api, and therefore we need the delegation pattern.

Goal of the mixin:

  • have a declarative notation that shows me what properties/methods and events will be delegated, and what added api host offers on top.
  • Reduce all the boilerplate (property syncing via templates/and or imperative boilerplate) and focus on the added functionality

Problems with the current mixin (DelegateMixin)

  • Getters and setters defined override those defined by UpdatingElement. Hooking into sync or async observers for delegated properties is thus not possible, since
 this.requestUpdate() is never called for them. Currently, only integrates with ObserverMixin (which should be opt-in mixin)
  • No support for documentation/Intellisense tools -> we should place properties in static get properties
  • Attribute delegation leads to problems and is not needed
    • Attributes are reflected to dom and affect a11y (especially if delegation target is light dom). Having two attributes aria-label or id breaks functionality (as an example, since those props/attrs should never be delegated in the first place)
    • Currently, attributes are read initially, deleted and put on delegation target. This is a non solution and leads to a confusing developer experience -> <lion-input name=“myField”></lion-input> -> it’s horrible DX that querySelector(‘[name=“myField”]’) now gives back the delegation target.
    • Delegation of attributes will be done indirectly (since the delegation target decides whether the prop should be reflected to attribute)
  • No state at host level: the host element is the SSOT interaction point for the Application Developer. Regardless of whether its child delegation targets are available, it should maintain state.
  • Supports only one delegation target. When a host holds multiple interactive targets and it becomes disabled, all its interactive children should become as well
  • Event delegation currently leads to weird behaviour. We should hide implementation details from Application Developer. Currently, we get the delegation target as the event.target. AD should not even know about the existence of delegation target. Plus, he/she just expects the host element here.
  • In some cases it turns out delegation targets (when put in light dom) are not reliably present at firstUpdated. When a delegationTarget becomes available, host should sync. DelegatesMixin should expose a method to resynchronise, so that slot-changed events that arrive lazily or something will be captured.

New mixin requirements:

  • Plays nicely with UpdateMixin (hooks into requestUpdate instead of overriding and aligns with static get properties)
  • Delegates props (and methods) via declarative config:
    • Methods can be put on property list as type Function
  • Exposes helper function for events:
    • A static config object doesn’t play well with docs/Intellisense. We should imperatively trigger with helper fn and annotate with @event jdsoc.
    • For events, we expose a function DelegatesMixin._delegateEvent(‘target-event’, targetNodeKey). This will listen for the event on target, cancel it and retrigger it under the same name on the host(will keep same characteristics, but sets target to host, since that is what is expected by AD).
  • Exposes helper function for syncing to (lazy) targets: DelegatesMixin._syncDelegationTarget(targetNodeKey) (also scheduled events are synced)
  • Supports multiple delegation targets: when I have a <field-listbox-dropdown>, I want to disable all interactive elements (button and listbox) whenever my hosts becomes disabled

[textarea] errorState is not being recalculated when the validators list changes

Upon adding and removing .errorValidators = ['required'] dynamically on textarea, when submitting the form without any value on textarea, the form is being submitted. The errorState is false. But, when trying to put something and removing the value before submitting the form, the required validation and working as expected.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.