elastic / eui

Elastic UI Framework 🙌
https://eui.elastic.co/
Other
6.08k stars 829 forks source link

<EuiButton> should be able to render links #24

Closed pugnascotia closed 6 years ago

pugnascotia commented 6 years ago

Sometimes we need to style a link like a button, which KuiButton can't do. react-bootstrap takes the approach of switch what element is rendered depending on whether an href is passed, so we could take that approach perhaps. I don't know if that gets tricker with e.g. react-router.

bevacqua commented 6 years ago

How about something like <KuiLink asButton={ true } />? In addition I'd also expose functions to build link classes and button classes

snide commented 6 years ago

KuiLink already does this. If you don't pass an HREF it becomes a button. Check the docs.

bevacqua commented 6 years ago

Beautiful

bevacqua commented 6 years ago

Reopening because <KuiLink> doesn't actually accomplish what we need, which would be a <KuiButton>-styled <a> tag.

I think doing <KuiButton href='/' /> should be possible, and it should render an anchor tag.

zinckiwi commented 6 years ago

+1. Ideally I'd like to see button styling (fill, size, etc.) decoupled from the button or a tag, so that any combination can by employed. Similar to EuiTitle vs the html hx tags, though in this case with only two realistic options we shouldn't need to completely isolate them in usage; rather, override the html tag via a prop in EuiLink (a -> button) or EuiButton (button -> a).

bevacqua commented 6 years ago

After working around this in Cloud for a while, I think it's best to have the ability to easily use EuiButton rendering with anything, such as React Router's <Link> or <button> or <a>, etc.

Here's what I went with for Cloud:

import React from 'react'
import { Link } from 'react-router'
import cx from 'classnames'

function CuiRouterLink({
  LinkTag,
  children,
  className,
  color = `primary`,
  ...other
}) {
  const classes = cx(`euiLink euiLink--${ color }`, className)

  return <LinkTag className={ classes } { ...other }>
    { children }
  </LinkTag>
}

export function CuiLink({ ...props }) {
  return <CuiRouterLink LinkTag={ Link } { ...props } />
}

// We can add a CuiIndexLink whenever that becomes necessary.
import React from 'react'
import { Link } from 'react-router'
import cx from 'classnames'

import { EuiIcon } from '@elastic/eui'

const sizeToClassNameMap = {
  s: `euiButton--small`,
  l: `euiButton--large`
}

function CuiButton({
  LinkTag,
  children,
  className,
  color = `primary`,
  iconType,
  size,
  fill = false,
  rawProps = {},
  buttonClass = `euiButton`,
  ...other
}) {
  const sizeClass = sizeToClassNameMap[size] || ``
  const classes = cx(`${ buttonClass } ${ buttonClass }--${ color }`, sizeClass, {
    [`${ buttonClass }--fill`]: fill
  }, className)

  const icon = iconType
    ? <EuiIcon className={ `${ buttonClass }__icon` } type={ iconType } />
    : null

  if (LinkTag === `input`) {
    return <LinkTag className={ classes } { ...other } { ...rawProps } />
  }

  return <LinkTag className={ classes } { ...other } { ...rawProps }>
    { icon }
    <span className={ `${ buttonClass }__content` }>
      { children }
    </span>
  </LinkTag>
}

export function CuiLinkButton({ ...props }) {
  return <CuiButton LinkTag='a' { ...props } />
}

export function CuiLinkButtonEmpty({ ...props }) {
  return <CuiButton LinkTag='a' { ...props } buttonClass='euiButtonEmpty' />
}

export function CuiRouterLinkButton({ ...props }) {
  return <CuiButton LinkTag={ Link } { ...props } />
}

export function CuiRouterLinkButtonEmpty({ ...props }) {
  return <CuiButton LinkTag={ Link } { ...props } buttonClass='euiButtonEmpty' />
}

export function CuiInputSubmitButton({ ...props }) {
  return <CuiButton
    LinkTag='input'
    rawProps={ {
      type: `submit`
    } }
    { ...props } />
}

export function CuiSubmitButton({ ...props }) {
  return <CuiButton
    LinkTag='button'
    fill={ true }
    rawProps={ {
      type: `submit`
    } }
    { ...props } />
}

While it's doable, I'd rather not have consumers needing to use EUI classes, what if they change or something? In it's current state, consuming EUI classes directly is less than optimal (there's way too many classes)

snide commented 6 years ago

FYI, still need this. @nreese had a need.

nreese commented 6 years ago

Another requested feature is to automatically append the attribute rel="noopener noreferrer" when prop target="_blank" https://github.com/elastic/kibana/pull/15995#issuecomment-356984388.