Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(Props Interfaces): add props interface to Button, AccordionTitle, AccordionContent and refactoring the types definitions #130

Merged
merged 13 commits into from
Aug 27, 2018
Merged
1 change: 1 addition & 0 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface IAccordionProps {
as?: any
activeIndex?: number[] | number
className?: string
children?: React.ReactNode
defaultActiveIndex?: number[] | number
exclusive?: boolean
onTitleClick?: (event: React.SyntheticEvent, data: IAccordionProps) => void
Expand Down
22 changes: 20 additions & 2 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
import * as PropTypes from 'prop-types'
import * as React from 'react'

import { UIComponent, childrenExist, customPropTypes } from '../../lib'
import { UIComponent, childrenExist, customPropTypes, Extendable } from '../../lib'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect /lib to be runtime utilities the library needs and /types to hold all our types. That would mean moving Extendable to /types as well.

There are a few more typing utilities in /types/theme.d.ts (ObjectOf, OneOrArray) that we could put in a separate /types/utils along with this Extendable util. It seems we should keep all these in one place.

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally agree 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added Extendable in the utils.d.ts file in the types and updated other components accordingly. Thanks for the suggestion!

import Icon from '../Icon'
import { ButtonBehavior } from '../../lib/accessibility'
import { Accessibility } from '../../lib/accessibility/interfaces'
import { ComponentVariablesInput, IComponentPartStylesInput } from '../../../types/theme'

export interface IButtonProps {
as?: any
children?: React.ReactNode
circular?: boolean
className?: string
disabled?: boolean
content?: React.ReactNode
fluid?: boolean
icon?: React.ReactNode | object | (React.ReactNode | object)[]
iconPosition?: 'before' | 'after'
onClick?: (event: React.SyntheticEvent, data: IButtonProps) => void
type?: 'primary' | 'secondary'
accessibility?: object | Function
styles?: IComponentPartStylesInput
variables?: ComponentVariablesInput
}

/**
* A button.
* @accessibility This is example usage of the accessibility tag.
* This should be replaced with the actual description after the PR is merged
*/
class Button extends UIComponent<any, any> {
class Button extends UIComponent<Extendable<IButtonProps>, any> {
public static displayName = 'Button'

public static className = 'ui-button'
Expand Down
13 changes: 10 additions & 3 deletions src/themes/teams/components/Avatar/avatarStyles.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { pxToRem } from '../../../../lib'
import { IComponentPartStylesInput, ICSSInJSStyle } from '../../../../../types/theme'
import { IAvatarProps } from '../../../../components/Avatar/Avatar'

const getAvatarDimension = (size: number) => {
return 12 + size * 4
Expand Down Expand Up @@ -49,7 +50,7 @@ const getAvatarFontSize = (size: number) => {
}

const avatarStyles: IComponentPartStylesInput = {
root: ({ props: { size } }): ICSSInJSStyle => ({
root: ({ props: { size } }: { props: IAvatarProps }): ICSSInJSStyle => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems that it is better for us to introduce general type for that as well, instead of inline type literal. Let me take a closer look to propose a suggestion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well generally the the object that we are receiving here is {props, variables} so we may introduce this, but the thing is this is not used everywhere, in some cases we have just the props, or nothing which isn't a problem, but when we have just the pros or nothing we immediately know that this part of the styles is somewhat static and doesn't depend on any prop of variable. So I am not sure if we wan to specify specific types which may won't be used...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we not wait until we figure out the optimal typing pattern for styles and variables before typing them? Especially as we plan to change this signature.

We haven't yet figured out how 3rd parties will create themes, but we know they will need access to component typings as well so that they can type their styles/variables as well. Not sure what the best way to type these is but I know from testing it myself that there are several ways to go about it, each with their own pros and cons.

I would propose that PRs opened against #117 for the purpose of typing props do not also attempt to type themes, just props for now.

Copy link
Contributor

@kuzhelov kuzhelov Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets try to follow the thought to see if we are on the same page. Seems that, at least semantically, the type of functions that are used for styling is the following (names are made up just as an example):

type StyleProvider<TProps, TVariables = any> = (args: StyleProviderArg) => ICSSInJSStyle`

type StyleProviderArg<TProps, TVariables = any> = { props: TProps, variables: TVariables }

To me it seems reasonable to declare this knowledge explicitly - because otherwise we would have the following issues for the following form:

root: ({ props: { size } }: { props: IAvatarProps }): ICSSInJSStyle =>
  • it is unclear for developer that variables parameter can be also consumed
  • an attempt to consume variables parameter will result in immediate need to change literal type. This will quickly become annoying with each new case :)
// compile-time error! type literal needs to be fixed as well
root: ({ props: { size }, variables }: { props: IAvatarProps }): ICSSInJSStyle =>
  • also an important factor that it will be much easier to read the following expression
root: ({ props: { size }}: StyleProviderArg<IAvatarProps>): ICSSInJSStyle =>

Please, let me know what do you think

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed to resolve this problem later, by means of dedicated PRs set

backgroundColor: 'inherit',
display: 'inline-block',
verticalAlign: 'top',
Expand All @@ -59,7 +60,7 @@ const avatarStyles: IComponentPartStylesInput = {
imageAvatar: (): ICSSInJSStyle => ({
verticalAlign: 'top',
}),
avatarNameContainer: ({ props: { size } }): ICSSInJSStyle => ({
avatarNameContainer: ({ props: { size } }: { props: IAvatarProps }): ICSSInJSStyle => ({
display: 'inline-block',
width: pxToRem(getAvatarDimension(size)),
height: pxToRem(getAvatarDimension(size)),
Expand All @@ -68,7 +69,13 @@ const avatarStyles: IComponentPartStylesInput = {
verticalAlign: 'top',
textAlign: 'center',
}),
presenceIndicatorWrapper: ({ props: { size }, variables: v }): ICSSInJSStyle => ({
presenceIndicatorWrapper: ({
props: { size },
variables: v,
}: {
props: IAvatarProps
variables: any
}): ICSSInJSStyle => ({
position: 'relative',
top: `-${pxToRem(getPresenceIndicatorWrapperTop(size))}`,
left: pxToRem(getPresenceIndicatorWrapperLeft(size)),
Expand Down
3 changes: 2 additions & 1 deletion src/themes/teams/components/Button/buttonStyles.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { pxToRem } from '../../../../lib'
import { IComponentPartStylesInput, ICSSInJSStyle } from '../../../../../types/theme'
import { disabledStyle, truncateStyle } from '../../../../styles/customCSS'
import { IButtonProps } from '../../../../components/Button/Button'

const buttonStyles: IComponentPartStylesInput = {
root: ({ props, variables }: { props: any; variables: any }): ICSSInJSStyle => {
root: ({ props, variables }: { props: IButtonProps; variables: any }): ICSSInJSStyle => {
const { circular, disabled, fluid, icon, iconPosition, type } = props
const primary = type === 'primary'
const secondary = type === 'secondary'
Expand Down