);
});

Modal.Types = {
  DANGER: 'danger',
  INFO: 'info',
  VALID: 'valid'
};

Modal.propTypes = {
  type: PropTypes.string.describe('Used to change the color of the modal and buttons. Use Modal.Types.DANGER, Modal.Types.INFO, or Modal.Types.VALID.'),
  icon: PropTypes.string.describe('The Icon to display in the top right corner.'),
  iconSize: PropTypes.number.describe('The size of the Icon in the top right corner.'),
  title: PropTypes.string.isRequired.describe('The title of the modal.'),
  subtitle: PropTypes.node.describe('The subtitle of the modal. Usually a string or <span>.'),
  cancelText: PropTypes.string.describe('String for the cancel button. Defaults to "Cancel".'),
  onCancel: PropTypes.func.describe('Called when the cancel button is clicked.'),
  canCancel: PropTypes.bool.describe('Determines whether this modal can be cancelled. Defaults to true. Useful to prevent the user from attempting to cancel a modal when a related request is in-flight.'),
  showCancel: PropTypes.bool.describe('Determines whether to show the cancel button. Defaults to true.'),
  confirmText: PropTypes.string.describe('String for the confirm button. Defaults to "Okay".'),
  onConfirm: PropTypes.func.describe('Called when the confirm button is clicked.'),
  disabled: PropTypes.bool.describe('If true, the confirm button will be disabled.'),
  progress: PropTypes.bool.describe('Passed to the confirm button.'),
  customFooter: PropTypes.node.describe('used to fill any custom footer use case.'),
  textModal: PropTypes.bool.describe('Used for modals that contain only text to pad the text.'),
  width: PropTypes.number.describe('custom width of modal.'),
  buttonsInCenter: PropTypes.bool.describe('If true, the buttons will appear in the center of the modal, instead of to the right. By default, the buttons appear on the right unless the modal contains no children, in which case they appear in the center.'),
};

export default Modal;
          </div>
        </div>

        <div className={styles.footer}>
          {footerButton}
        </div>
      </div>
    );
  }
}

ExplorerQueryComposer.propTypes = {
  query: PropTypes.object.describe(
    'The query to be edited by this composer.'
  ),
  onSave: PropTypes.func.isRequired.describe(
    'Function to be called on query created/saved.'
  ),
  onDismiss: PropTypes.func.describe(
    'Function to be called on dismiss button clicked.'
  ),
  isNew: PropTypes.bool.describe(
    'True if the composer is trying to compose a new query. ' +
    'False if the composer is editing an existing one.'
  ),
  isTimeSeries: PropTypes.bool.describe(
    'If set to true, add default group (day, hour) and aggregate to the composer. ' +
    'Otherwise, render limit inside the composer.'
  )
}
            onChange('');
          }
        }}
        onChange={e => {
          let newValue = e.target.value;
          if (VALID_REGEX.test(newValue)) {
            onChange(newValue.replace(/\s/g, ''));
            this.setState({cursorPosition: e.target.selectionStart});
          } else {
            //If they try to type a non-digit, don't move the cursor.
            this.setState({cursorPosition: e.target.selectionStart - 1});
          }
        }} />
    );
  }
};

export default CreditCardInput;

CreditCardInput.propTypes = {
  value: PropTypes.string.describe(
    'The current value of the controlled input.'
  ),
  lastFour: PropTypes.string.describe(
    'If provided, and the current value is falsy, the input will render as "•••• •••• •••• {lastFour}"'
  ),
  onChange: PropTypes.func.describe(
    'A function called when the input is changed.'
  )
};
          });
        });
      }}
    />;

    let footer = shouldShowFooter ? <FlowFooter
      primary={saveButton}
      secondary={secondaryButton({ setField })}
      errorMessage={errorMessage}>
      {footerMessage}
    </FlowFooter> : null;

    return <div>{form}{footer}</div>;
  }
}

FlowView.propTypes = {
  initialChanges: PropTypes.object.describe('A map of field names to their initial changed values (where applicable), used to seed the form'),
  initialFields: PropTypes.object.isRequired.describe('A map of field names to their starting values. For a creation form, this is probably an empty object.'),
  renderForm: PropTypes.func.isRequired.describe('A function used to render the body of the form. It receives an object with fields (the current form state), changes (an isolated set of changes), setField(name, value) (a method for updating a field), and resetFields().'),
  footerContents: PropTypes.func.isRequired.describe('A function that renders the message in the footer. It receives an object with fields (the current form state) and changes (an isolated set of changes). This will only be called if some changes have been made.'),
  inProgressText: PropTypes.string.describe('Text for commit button when request is in progress. Default is "Save changes".'),
  submitText: PropTypes.string.describe('Text for commit button when filling out form. Default is "Saving\u2026"'),
  onSubmit: PropTypes.func.describe('Function to call when submitting the FlowView. Must return a promise. It receives an object with fields (the current form state) and changes (an isolated set of changes).'),
  afterSave: PropTypes.func.describe('Function to call after saving succeeds. It receives the fields, setField(), and resetFields(). Use this if you require custom modification to fields after save succeeds (eg. in PushSettings we clear the GCM credentials fields after they are saved)'),
  validate: PropTypes.func.describe('Function that validates the form. If it returns a non-empty string, that string is display in the footer, and the submit button is disabled. You can return "use default" to disable the button and use the default message (not in red), but you really shouldn\'t.'),
  showFooter: PropTypes.func.describe('Recieves the changes, and returns false if the footer should be hidden. By default the footer shows if there are any changes.'),
  secondaryButton: PropTypes.func.describe('Overrride the cancel button by passing a function that returns your custom node. By default, the cancel button says "Cancel" and calls resetFields().'),
  defaultFooterMessage: PropTypes.node.describe('A message for the footer when the validate message is "use default"'),
};
  }
  let className = state === SaveButton.States.FAILED ? shake : null;
  return <span className={className}><Button
    primary={true}
    width={'128px'}
    progress={state === SaveButton.States.SAVING}
    color={color}
    onClick={state === SaveButton.States.WAITING ? onClick : null}
    value={value}
    disabled={state === SaveButton.States.WAITING ? disabled : false}
    {...buttonProps}/>
  </span>;
};

SaveButton.States = keyMirror(['SAVING', 'SUCCEEDED', 'FAILED']);

let {primary, width, progress, color, onClick, value, ...forwardedButtonProps} = Button.propTypes;
delete forwardedButtonProps.value;
SaveButton.propTypes = {
  state: PropTypes.string.describe('SaveButton.States.(SAVING|SUCCEEDED|FAILED|WAITING). Defaults to WAITING.'),
  onClick: PropTypes.func.describe('Click handler. Actived if button is clicked while enabled and in WAITING state.'),
  waitingText: PropTypes.string.describe('Text for WAITING state. Defaults to "Save changes".'),
  savingText: PropTypes.string.describe('Text for SAVING state. Defaults to "Saving\u2025".'),
  failedText: PropTypes.string.describe('Text for FAILED state. Defaults to "Save failed".'),
  succeededText: PropTypes.string.describe('Text for SUCCEEDED state. Defaults to "Saved!".'),
  disabled: PropTypes.bool.describe('Disables button if in WAITING state.'),
  ...forwardedButtonProps,
};

export default SaveButton;
        </div>
        <svg width={chartWidth + 10} height={chartHeight + 10}>
          <g>{labelHeights.map((h) => <path key={'horiz_' + h} d={'M0 ' + h + ' H' + chartWidth} style={{ stroke: '#e1e1e1', strokeWidth: 0.5 }} />)}</g>
          <path d={'M0 ' + chartHeight + ' H' + chartWidth} style={{ stroke: '#e1e1e1', strokeWidth: 1 }} />
          <g>{tickPoints.map((t, i) => <path key={'tick_' + i} d={`M${t} ${chartHeight} V ${chartHeight - 10}`} style={{ stroke: '#e1e1e1', strokeWidth: 1 }} />)}</g>
          {groups}
        </svg>
        {popup}
      </div>
    );
  }
}

Chart.propTypes = {
  width: PropTypes.number.isRequired.describe(
    'The width of the chart.'
  ),
  height: PropTypes.number.isRequired.describe(
    'The height of the chart.'
  ),
  data: PropTypes.object.isRequired.describe(
    'The data to graph. It is a map of data names to objects containing two keys: ' + 
    '"color," the color to use for the lines, and "points," an array of tuples containing time-value data.'
  ),
  formatter: PropTypes.func.describe(
    'An optional function for formatting the data description that appears in the popup. ' +
    'It receives the numeric value of a point and label, and should return a string. ' +
    'This is ideally used for providing descriptive units like "active installations."'
  )
};
      );
    } else {
      content = (
        <div className={styles.range}>
          <span>{this.rangeString()}</span>
          <Icon width={18} height={18} name='calendar-solid' fill='#169CEE' />
        </div>
      );
    }

    return (
      <div className={styles.wrap} onClick={this.toggle.bind(this)}>
        {content}
        {popover}
      </div>
    );
  }
}

DateRange.propTypes = {
  value: PropTypes.object.describe(
    'The value of the range. It has two props, "start" and "end," which are both Dates.'
  ),
  onChange: PropTypes.func.describe(
    'A function called when the date range is closed. It receives an object with two Date properties: start and end.'
  ),
  align: PropTypes.string.describe(
    'The side to align the range selector with. Possible options are Constants.Directions.LEFT or Constants.Directions.RIGHT.'
  ),
};
            </div>
          </div>
        </Popover>
      );
    } else {
      content = (
        <div className={styles.chrome}>
          <span>{`${monthDayStringUTC(this.props.value)}`}</span>
          <Icon width={18} height={18} name='calendar-solid' fill='#169CEE' />
        </div>
      );
    }

    return (
      <div className={styles.wrap} onClick={this.toggle.bind(this)}>
        {content}
        {popover}
      </div>
    );
  }
}

ChromeDatePicker.propTypes = {
  value: PropTypes.object.describe(
    'The Date value of the picker.'
  ),
  onChange: PropTypes.func.describe(
    'A function called when the date picker is changed. It receives a new Date value.'
  ),
};