Giter Club home page Giter Club logo

presenters's People

Contributors

dennismonsewicz avatar dependabot[bot] avatar dsgraham avatar jadefish avatar kgrafius avatar nullterminator avatar rx avatar semantic-release-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

presenters's Issues

Errors if POMs don't include plugins that poms they attach use

Describe the bug
When a pom attaches another that uses a plugin with Javascript the browser throws errors because the Javascript doesn't get included.

To Reproduce

Voom::Presenters.define(:some_pom) do
  plugin :some_plugin_with_js
end

Voom::Presenters.define(:another) do
  attach :some_pom
end

Render :another in browser and there will be a Javascript undefined error. Currently including the plugin in :another loads the files and clears the error, but this isn't ideal.

Expected behavior
The POM renders clean. JS from atached pom's plugins is included.

Ability to render text without markdown

Text from a DB or other source with values that include markdown-like text will render in a markdown fashion. For example, text address[:ln2] where address[:ln2] = "#1B" renders 1B as an <h1>.

Describe the solution you'd like
I don't know if markdown should be the default. I suspect that making it not the default could introduce a ton of other problems with already created infrastructure though. A raw_text component or an option on text/body would probably work.

Support maxlength HTML element for text_field component

As a developer, who is creating a form with a max length requirement for an input field, I would like the ability to pass in a maxlength attribute when creating a text_field.

Proposed Change

Component Class

require 'voom/presenters/dsl/components/input'

module Voom
  module Presenters
    module DSL
      module Components
        class TextField < Input

          attr_reader :required, :full_width, :password, :auto_complete

          def initialize(**attribs_, &block)
            super(type: :text_field, **attribs_, &block)
            @required = attribs.delete(:required){ false }
            @full_width = attribs.delete(:full_width){ true }
            @password = attribs.delete(:password){ false }
            @maxlength = attribs.delete(:maxlength) { nil }
            @auto_complete = validate_auto_complete(attribs.delete(:auto_complete) { :off })
            label(attribs.delete(:label))if attribs.key?(:label)
            value(attribs.delete(:value))if attribs.key?(:value)
            expand!
          end

          def label(text=nil)
            return @label if locked?
            @label = text
          end

          def icon(icon=nil, **attribs, &block)
            return @icon if locked?
            @icon = Components::Icon.new(parent: self, icon: icon, position: attribs.delete(:position){:right},
                                         **attribs, &block)
          end

          def value(value=nil)
            return @value if locked?
            @value = value
          end

          def pattern(pattern=nil)
            return @pattern if locked?
            @pattern = json_regexp(Regexp.new(pattern))
          end

          def hint(hint=nil)
            return @hint if locked?
            @hint = hint
          end

          def error(error=nil)
            return @error if locked?
            @error = error
          end

          private
          def json_regexp(regexp)
            str = regexp.inspect.
                sub('\\A', '^').
                sub('\\Z', '$').
                sub('\\z', '$').
                sub(/^\//, '').
                sub(/\/[a-z]*$/, '').
                gsub(/\(\?#.+\)/, '').
                gsub(/\(\?-\w+:/, '(').
                gsub(/\s/, '')
            Regexp.new(str).source
          end

          def validate_auto_complete(value)
            case value
            when false, :disabled, 'disabled', 'off', nil
              :off
            when true, :enabled, 'enabled', 'on'
              :on
            else # :on, :off, client-specific values
              value
            end
          end
        end
      end
    end
  end
end

Component HTML

<% if comp
     leading_icon = comp.icon && comp.icon.position.select {|p| eq(p, :left)}.any?
     trailing_icon = comp.icon && comp.icon.position.select {|p| eq(p, :right)}.any?
     auto_complete = comp.auto_complete&.to_sym == :off ? 'extra-off' : comp.auto_complete
%>
  <div id="<%= comp.id %>"
       <% if comp.tag %>data-input-tag="<%= comp.tag %>"<% end %>
       <% if comp.dirtyable %>data-dirtyable<% end %>
       class="v-input v-text-field v-focusable mdc-text-field mdc-text-field--outlined
            <%= 'mdc-text-field--with-leading-icon' if leading_icon %>
            <%= 'mdc-text-field--with-trailing-icon' if trailing_icon %>
            <%= 'mdc-text-field--disabled' if comp.disabled %>
            <%= 'is-invalid is-dirty' if comp.error %>"
       style="<%= 'width:100%' if comp.full_width %>">

    <input id="<%= comp.id %>-input"
           name="<%= comp.name %>"
           type="<%= comp.password ? 'password' : 'text' %>"
           value="<%= comp.value %>"
           class="mdc-text-field__input"
           aria-controls="<%= comp.id %>-input-helper-text"
           <% if comp.disabled %>disabled<% end %>
           <%= 'required' if comp.required %>
           <%= 'invalid' if comp.error %>
           <%= "pattern='#{comp.pattern}'" if comp.pattern %>
           <%= "maxlength='#{comp.maxlength}'" if comp.maxlength %>
           autocomplete="<%= auto_complete %>"
           list="<%= comp.id %>-list"
           <%= erb :"components/event", :locals => {comp: comp, events: comp.events, parent_id:  "#{comp.id}-input"} %>>

    <%= erb :"components/icon", :locals => {comp: comp.icon, class_name: 'mdc-text-field__icon', parent_id: "#{comp.id}-input"} %>

    <div class="mdc-notched-outline">
      <div class="mdc-notched-outline__leading"></div>
      <div class="mdc-notched-outline__notch">
        <label for="<%= comp.id %>-input" class="mdc-floating-label"><%= comp.label %></label>
      </div>
      <div class="mdc-notched-outline__trailing"></div>
    </div>
    <datalist id="<%= comp.id %>-list">
    </datalist>
  </div>
  <% if comp.error || comp.hint %>
    <p id="<%= comp.id %>-input-helper-text" class="mdc-text-field-helper-text mdc-text-field-helper-text--persistent mdc-text-field-helper-text--validation-msg" aria-hidden="true">
      <%= comp.error || comp.hint %>
    </p>
  <% end %>
  <%= erb :"components/tooltip", :locals => {comp: comp.tooltip, parent_id: comp.id} %>
<% end %>

Validation on components and plugins is not run

Describe the bug
In a post event any components or plugins that provide a validate function should be called, but they are not. Only the component that triggers the event has its validate function run.

Expected behavior
All input components included in a post event should be validated if they provide a validate function.

Implement a "link" component

Is your feature request related to a problem? Please describe.
Currently, links are only created via a block with

event :click do
  loads :other_presenter
end

which is alright, but causes a problem in that, when rendered in the web client, each link is merely an <a> tag with a link to javascript:void(0). This breaks the web for anyone who might want to open a link in a new tab, an incognito window, copy the URL, etc.

Describe the solution you'd like
loads is important and needs to stay, but I'd like to see a link or other noun/verb component implemented, such that I could do:

link to: :other_presenter do
  icon :fa_cog, position: :left 
  body "My Link"
end

and have that render as an <a> tag.

Break out rich text area into separate plugin

The rich text area component should be its own plugin.

Rationale

The rich text area component:

  1. depends on an external JS library (Quill)
  2. was developed before plugins existed
  3. is more configurable than the POM abstraction layer comfortably allows
  4. should be substitutable for other rich text editing JS libraries

The rich text area as a built-in component doesn't support points 3 and 4 above.

Rich text areas always report themselves as dirty

Describe the bug
Rich text areas with initial values report as dirty before any changes have been made.

To Reproduce
Steps to reproduce the behavior:

  1. Create a rich text area with an initial value:
content do
  rich_text_area do
    value 'here is some default text'
  end

  button :cancel do
    event :click do
      prompt_if_dirty :some_dialog
      # ...
    end
  end
end
  1. Click the cancel button to run the prompt_if_dirty check

Expected behavior
The prompt_if_dirty check should not invoke :some_dialog.

Desktop:

  • OS: macOS 10.14 Mojave
  • Browser: Chrome
  • Version: 72

Fix Validations for text fields

When you specify a pattern and error message for a text field the error message is always being displayed and then has a color change when the text_field fails the pattern match. I would expect that the message would remain hidden until the contents fail the patten match at which time the message would be displayed with appropriate error msg color.

Browser back button to a form with a text field with a label does not load the label

Steps to Reproduce

  1. Create a POM with a form, a text field inside the form, and a label on the form.
  2. Give the form a posts action (on a button), with a redirect. 3.
    Complete the form with some value in the text field, engaging the redirect.
  3. Then click "Back" on your browser.
  4. The form and field are loaded with the previous value, but the label is not loaded into the top of the text field.

Expected behavior
The label should be loaded.

Other
A similar but different problem can be seen in the presenters demo:

  1. Go to https://powerful-bastion-96181.herokuapp.com/text_fields and type something in the first field.
  2. Click away to another page.
  3. Click browser's back button.
  4. Text field loads with a weird border (but does have the label).

Desktop (please complete the following information):

  • OS: Mac OSX Mojave
  • Browser Chrome
  • Version 72

Pre-filled text fields have incorrect labels

Describe the bug
When you land on a page with a text field that is pre-filled by the browser, its label stays in the "empty" position and is unreadable until it receives focus.

To Reproduce
Steps to reproduce the behavior:

  1. Go to a page with text field and Chrome's autofill turned on
  2. See the behavior of autofilled fields

Expected behavior
Fields with input should have readable labels along the top border.

Screenshots

screen shot 2019-03-04 at 9 11 09 am

Desktop (please complete the following information):

  • OS: MacOS 10.14.3
  • Browser: Chrome
  • Version: 72.0.3626.109

Buttons should be disabled until all actions in their event block complete

Describe the bug
If you have a button with an event :click that does a POST and then loads another presenter/page, the button will become clickable immediately after the POST completes, which may not be desirable. The button should only be enabled after all action in the event block have completed

Additional context
See Geotix pivotal ticket #169553198

Post toggle off values

In the webclient checkbox/toggle controls don't post when in an off state.

All toggles should always post a name and value. The value should be off if no other value is provided.

Rework Typography component render semantic HTML markup

Is your feature request related to a problem? Please describe.
Inside of the ExpansionPanel component there is the ability to render text and secondary text elements. Each element is currently hard coded as semantic HTML as span tags. It would be nice to simply render the respective Typography component and pass in the local attributes it cares about.

Describe the solution you'd like
I'd like to either A) rebuild the typography component into multiple ones (H1, H2, H3, Span, etc) to support semantic HTML components or B) rebuild the typography component in the interim to render <span> tags instead of <div> elements.

Describe alternatives you've considered
I have simply just modified the ExpansionPanel markup to support the attributes I care about

Additional context
N/A

Double parameters + 'null' parameters

The events loads, updates, posts, delete and replaces has been adding two parameters to the URL's. One happens in the URL creation by the url call in the dsl objects. (It delegates to the router.)
The second one is added by the client side javascript.

Fix this so that parameters are put on the url for loads, replaces and deletes
And add parameters to post body for updates and deletes.

Another related issues, a nil value will get posted back as a null string.
Posts and Updates should post key=
Loads, replaces and delete should add &key& to the URL.
This ensures the presence of the key can have meaning, but the value will be nil. (And avoids fix-up code dealing with 'null' somewhere else.)

Refactor form-field component

A form-field should not be a container component. The fact that it is a container to single input is unnecessary, confusing and is causing extra code to check for this case.
The MDC component should be initialized as part of the input component.

Control values should properly escape hash and arrays

Use case: I'm using a chip to submit a name/value pair. The value I want to pass is a hash (or array). I expect that it will properly escape my ruby hash or array into query string that will come back into my rails controller or sinatra route params in the same format.

FWIW - A workaround is to json encode the values and decode on the way back in.

Progress Bars

Automatic display of progress bars on posts/updates/deletes/loads.

Toggle label is misaligned

The label element contained within toggle components is misaligned when embedded in an element smaller than 12 column-widths:

screen shot 2018-11-08 at 13 50 14

dialog width: '30rem' do
  form do
    switch name: :reset_password_email, checked: true, text: 'Send reset password email'
  end
end

When contained within a column of size 12, the label lacks any leading space:

screen shot 2018-11-08 at 13 57 54

grid do
  column size: 12 do
    switch name: :reset_password_email, checked: true, text: 'Send reset password email'
  end
end

Text areas' original values are escaped incorrectly

Describe the bug
The data-original-value attribute for text area components (text area, rich text area) is not escaped correctly and can bleed out of its attribute value. This results in an incomplete attribute value, false positive for component dirty checks, and creation of attributes on the element named after the contents of the text area.

To Reproduce
Steps to reproduce the behavior:

  1. Find or create a text area with an original value which contains one or more double quote characters (") (e.g., most source code text areas in the Demo)
  2. Inspect the text area element
  3. Observe the malformed data-original-value attribute

Expected behavior
The data-original-value attribute should not bleed out and create extraneous attributes.

Screenshots


Create ContainerBase class

Create a container base class for components such as content, form, card, dialog, etc. and move common code into it.

List line actions can trigger multiple events

If you have a list line with a click event and then an action on that line with a different event, multiple actions can be triggered. For example:

list do
  things.each do |thing|
    line do
      text thing.name
      event :click do
        hide :this_content_block
        show :some_other_block
      end
      actions do
        switch name: :active, value: true, off_value: false, checked: thing.active do
          event :change do
            posts update_thing_path, thing_id: thing.id
          end
        end
      end
    end
  end
end

Clicking the switch on a line will cause the post action to fire, but also the line's click event actions. Changing the switch's event to click instead of change makes it work as expected; the post fires, the hide/show does not.

MDC Slider Render issue

If the MDC version of the slider component is initially rendered as hidden (inside a div with block: none) it does not initialize properly. A resize of the page will trigger the MDCSlider component to recalculate its layout and fixes the problem. I created a slider-render-issue branch that demonstrates the problem in demo/components.sliders.pom

Webclient: position buttons

grid do
    column 6 do
      headline 'Outside Form'
      text_field
      text_field
      button 'Cancel', position: :right # BUG - filed as issue 
      button 'Continue', type: :raised, position: :right
    end
end

This results in the cancel button being hidden by the continue button.
Expected: Inline elements floating to the right would appear side by side.

Need a consistent mixin for containers

Describe the bug
The common mixin is not comprehensive enough to give consistent behavior across all types of containers. For example; a text field is valid inside a content block, but not within a dialog

Expected behavior
Anything that can be used as a container - content form dialog - should allow that same children and behave consistently.

All form buttons are rendered as submit buttons

All buttons rendered by the web client are missing the type attribute, indicating to browsers that they're all submit buttons. Per https://www.w3.org/TR/html5/forms.html#the-button-element:

The type attribute controls the behavior of the button when it is activated. [...]
The missing value default is the submit button state.
If the type attribute is in the submit button state, the element is specifically a submit button.

This causes a number of issues.

  • submitting the form executes the action of the first button within the form
  • reset buttons (type="reset") cannot be rendered
  • normal (non-submit) buttons (type="button") cannot be rendered

Migrate Dialog to MDC

Currently dialogs are using MDL implementation in the webclient.
That does not work on safari correctly.

Eliminate Form tag

Question: Should we continue to use the form html element anymore?

Background: You can submit grouped input in a number of ways from the POM.

  1. Using a Form block around the input
  2. Using another container element - Content, Dialog and Card
  3. Using tagged input

Content, dialog and card behavior does not use html forms.
One of the biggest issues is that nested forms on chrome (and possibly other browsers) are collapsed in the markup, the result is that the inner form tag is discarded.
To be more consistent, and not have special case for forms, we should eliminate the form behavior and alias the form POM keyword to content.

Other notes:

  • Form validation should be done at the component level and not rely on form html tag implementation.
  • Default button submit behavior should be driven by the first 'raised' button in the POM.
  • Focus and Tab should work across any container type.

Table "select all" needs select visible/select total function

The current select all checkbox in a table header only selects all the checkboxes visible on the page. We need to add the functionality currently part of the selectable list, where you can toggle on/off selection of the entire results set across all pages.

Edge: InputEvent is not supported

Describe the bug
The web client's replaces event (VReplaces) does not work correctly in Edge 44 and earlier.

To Reproduce
Steps to reproduce the behavior:

  1. With the JS console open, go to https://powerful-bastion-96181.herokuapp.com/replaces_action
  2. Click on Replaces
  3. Observe the error:
If you got here it may not be what you think: ReferenceError: 'InputEvent' is not defined

Expected behavior
The Replaces button correctly replaces the target content area.

Desktop:

  • OS: Windows 10 (1809)
  • Browser: Edge
  • Version: 44.17763.1.0 (EdgeHTML 18.17763)

Additional context
Edge does not support InputEvent: https://developer.mozilla.org/en-US/docs/Web/API/InputEvent#Browser_compatibility

Submitting forms with empty selects yields 'null' string literal

If a form contains a <select> with no <option> elements, submitting the form yields a corresponding key-value pair with a string literal 'null' value. Non-empty selects and empty non-select elements yield the empty string.

Example:

form do
  select name: :select1 do
    label 'Select1'

    # first item selected by default:
    some_array.each_with_index do |item, i|
      option text: item, value: i
    end
  end

  select name: :select2 do
    label 'Select2'

    # no options:
    [].each_with_index do |item, i|
      option text: item, value: i
    end
  end

  button 'Submit' do
    creates some_path, redirect: some_other_path
  end
end

Submitting the above form yields:

select1: 0
select2: "null"

Refactor error collection and display

Problem

The current process for aggregating and displaying errors from server responses is not capable of handling deeply-nested error structures (e.g. nested Dry validation schemas, arrays).

Additionally, only client-side errors are visually associated with their failing element.

Context

The current implementation yields the following behavior:

  • Nested error structures result in displaying [object Object] in the nearest error <div> element.
  • All errors collected from server responses are displayed in the nearest error <div> element instead of close to or within each error's associated element.

Proposed solutions

  • All error collection and display should be capable of handling errors structures of arbitrary depth.
  • Errors collected from responses should be displayed alongside their associated element.
  • Page-level errors (the nearest error <div> element behavior) should be used only when an error cannot be associated with an element (e.g., errors describing a loss of network connectivity).

Buttons which only have a "loads" action should render an appropriate href in webclient

I'm often frustrated when I make a link via a button -> loads action, or a list -> item in presenters but in the webclient, this basic link is rendered with an href of javascript:void(0) or blank, or as an HTML button and I can't do some standard browser things like middle-click it and such.

I don't know if there's a simple fix for this. Basically anything that could take a click event would need to be checkable and rendered differently if and only if the only click action is a loads. That part might not be too hard. But, there's a ton of CSS changes that could/would be involved in such an endeavor. Standard buttons currently render as the HTML button tag but these would need to be changed over to be a tags that look like buttons for this to work.

Plugins are not reloaded

When using a plugin in a POM, changing files causes the plugin to stop functioning.

Voom::Presenters.define(:some_pom) do
  plugin :some_plugin
  # ...
end

Changing any file (including the POM itself) causes the autoloader to reload everything. Plugins seem to not be included in the reload sweep, however, and the POM fails to render at next page load:

NameError at /some/path
undefined local variable or method `some_plugin'

This can be worked around by require_depdency'ing the plugin's main .rb file and any files referenced therein:

Voom::Presenters.define(:some_pom) do
  require_dependency 'voom/presenters/plugins/some_plugin.rb'
  require_dependency 'voom/presenters/plugins/some_plugin/component.rb'
  plugin :some_plugin
  # no more NameError
end

All web client requests specify Accept: */*

All requests issued by the web client specify */* as the value for the Accept header. This value should be configurable (for POST requests, at least), allowing users to specify a preferred value.

keydown event handlers for specific keys

You should be able to bind an event handler to a keydown event for a specific set of keys.
Examples - using enter key for submit, providing a keyboard friendly set of options.
Single key:

event :keypress, :enter do
    # event handler code goes here
end

An array of keys:

event :keypress, :enter, :g do
    # event handler code goes here
end

Having a modifier

event :keypress, :enter, {key: :g, modifiers: :ctrl} do
    # event handler code goes here
end

Having multiple modifiers

event :keypress, :enter, {key: :g, modifiers: [:shift,:ctrl]} do
    # event handler code goes here
end

Init events does not always behave as expected

Describe the bug
If an event is bound to element inside a component it may not get initialized as expected. For example, in the select component the events are bound to the select element, when the component is initialized they are not found.

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.